package com.eralink.unionuhfreader;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.OpenableColumns;
import android.serialport.SerialPortFinder;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TabHost;
import android.widget.TabWidget;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.eralink.uhfreaderlib.EraLinkUHFReaderApiError;
import com.eralink.uhfreaderlib.EraLinkUHFReaderCmdCode;
import com.eralink.uhfreaderlib.EraLinkUHFReaderInterface;
import com.eralink.uhfreaderlib.EraLinkUHFReaderReportCallback;
import com.eralink.unionuhfreader.adapter.AutoReadTagListViewAdapter;
import com.eralink.unionuhfreader.adapter.InventoryTagListViewAdapter;
import com.eralink.unionuhfreader.adapter.MultiBankAutoReadTagListViewAdapter;
import com.eralink.unionuhfreader.adapter.ReaderListViewAdapter;
import com.eralink.unionuhfreader.bean.EPCBean;
import com.eralink.unionuhfreader.bean.ReaderBean;
import com.eralink.unionuhfreader.beep.Beep;
import com.eralink.unionuhfreader.util.CacheNode;
import com.eralink.unionuhfreader.util.CachePool;
import com.eralink.unionuhfreader.util.EPCReportDataParseUtil;
import com.eralink.unionuhfreader.util.EralinkApiErrDefinition;
import com.eralink.unionuhfreader.util.ErrString;
import com.eralink.unionuhfreader.util.StringUtils;
import com.eralink.unionuhfreader.util.Utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity implements EraLinkUHFReaderReportCallback {
    private static final String TAG = "MainActivity";

    // { 主页
    private final int[] main_layouts = { R.id.tab_reader_page, R.id.tab_tag_page,R.id.tab_device_control_page,R.id.tab_system_config_page, R.id.tab_report_page};//
    private static final String[] main_layout_titles = {"读写器", "标签","设备控制", "系统配置", "上报"};//,
    private TabWidget mainTabWidget;
    // 主页 }

    // { 读写器
    private Spinner spinnerReaderWokeRole;
    private LinearLayout llReaderAsServer;
    private LinearLayout llReaderAsClient;
    private Spinner spinnerReaderConnectMode;
    private LinearLayout llUartCommunicationGroup;
    private LinearLayout llNetworkCommunicationGroup;
    private EditText edtReaderIP;
    private EditText edtReaderPort;
    private Spinner spinnerUart;
    private Spinner spinnerConnectBaud;
    private Button btnSearchReader;
    private Button btnConnectReader;
    private Button btnDisconnectReader;

    private EditText edtListenIP;
    private EditText edtListenPort;
    private Button btnStartListen;
    private Button btnStopListen;
    private final List<ReaderBean> readerList = new LinkedList<>();
    private final Hashtable<String, ReaderBean> readerMap = new Hashtable<>() ;
    private ReaderListViewAdapter readerListViewAdapter;
    private Button btnRemoveReader;

    private byte reader_id = 0;
    private int reader_work_role = -1; // 0, server; 1, client
    // 读写器 }

    // { 标签
    private final int[] tag_layouts = { R.id.tab_read_tag_page, R.id.tab_tag_operation_page,
            R.id.tab_tag_content_inventory_page};
    private static final String[] tag_layout_titles = { "读标签", "标签操作", "内容盘寻" };
    private TabWidget tagTabWidget;
    // 标签 }

    // { 读标签
    private final int[] read_tag_layouts = { R.id.tab_inventory_page, R.id.tab_auto_read_page};
    private static final String[] read_tag_layout_titles = { "读卡", "自动读卡" };
    private TabWidget readTagTabWidget;
    // 读标签 }

    // { 标签盘点
    private final List<EPCBean> epcBeanList = new LinkedList<>();
    private final Hashtable<String, EPCBean> epcBeanMap = new Hashtable<>();
    private InventoryTagListViewAdapter epcListViewAdapter;
    private TextView tvInventoryElapsedTime;
    private TextView tvInventoryRate;
    private TextView tvInventoryTagNumber;
    private TextView tvInventoryTagReportTotalNumber;
    private Button btnInventoryOnce;
    private Button btnInventory;
    private Button btnStopInventory;

    private Timer inventoryStatisticsTimer;
    private final AtomicLong inventoryElapsedTime = new AtomicLong(0);
    private final AtomicInteger tagReportTotalNumber = new AtomicInteger(0);
    private final AtomicInteger statisticsIntervalTagNumber = new AtomicInteger(0);
    private final AtomicInteger countForInventoryRate = new AtomicInteger(0);

    private TextView tvInventoryStatisticsDuration;
    private TextView tvInventoryStatisticsQueryCount;
    private TextView tvInventoryStatisticsReceivedRN16Count;
    private TextView tvInventoryStatisticsRN16TimeoutCount;
    private TextView tvInventoryStatisticsEPCTimeoutCount;
    private TextView tvInventoryStatisticsTagReadCount;
    private TextView tvInventoryStatisticsEPCCrcCount;
    private TextView tvInventoryStatisticsInterfererBlockingDuration;
    private TextView getEdtInventoryStatisticsInterfererBlockingCount;
    private Button btnGetInventoryStatistics;

    private volatile boolean inventorying = false;
    private long lastMarkTime = 0;

    private static final int cmd_update_epc = 0x01;
    private static final int cmd_update_inventory_statistic = 0x02;
    private static final int cmd_report_io_failed = 0x03;
    private static final int cmd_auto_read_update = 0x04;
    private static final int cmd_update_auto_read_statistic = 0x05;
    private static final int cmd_multi_bank_auto_read_update = 0x06;
    private static final int cmd_reader_connect = 0x07;
    private static final int cmd_reader_disconnect = 0x08;
    private static final int cmd_others_report = 0xFF;
    // 标签盘点 }

    // { 自动读卡
    private final List<EPCBean> autoReadEpcBeanList = new LinkedList<>();
    private final Hashtable<String, EPCBean> autoReadEpcBeanMap = new Hashtable<>() ;
    private AutoReadTagListViewAdapter autoReadEpcListViewAdapter;
    private TextView tvAutoReadElapsedTime;
    private TextView tvAutoReadRate;
    private TextView tvAutoReadTagNumber;
    private TextView tvAutoReadTagReportTotalNumber;
    private TextView tvAutoReadPcHead;
    private TextView tvAutoReadEpcHead;
    private TextView tvAutoReadRssiHead;
    private TextView tvAutoReadTidHead;
    private TextView tvAutoReadUserHead;
    private Button btnStartAutoRead;
    private Button btnStopAutoRead;

    private Timer autoReadStatisticsTimer;
    private final AtomicLong autoReadElapsedTime = new AtomicLong(0);
    private final AtomicInteger autoReadTagReportTotalNumber = new AtomicInteger(0);
    private final AtomicInteger autoReadStatisticsIntervalTagNumber = new AtomicInteger(0);
    private final AtomicInteger countForAutoReadRate = new AtomicInteger(0);

    private volatile boolean autoReading = false;
    private long autoReadLastMarkTime = 0;

    private boolean autoReadTriggeredByManual = false;
    // 自动读卡 }

    // { 标签操作
    private EditText edtTagAccessPassword;
    private Spinner spinnerTagAccessBank;
    private EditText edtTagAccessOffset;
    private EditText edtTagAccessLength;
    private EditText edtTagData;

    private Spinner spinnerTagLockType;
    private Spinner spinnerTagLockBank;

    private EditText edtTagKillPassword;

    private Spinner spinnerTagSelectBank;
    private EditText edtTagSelectOffset;
    private EditText edtTagSelectLength;
    private EditText edtTagSelectData;
    // 标签操作 }

    // { 多区域盘点
    private final List<EPCBean> multiBankAutoReadEpcBeanList = new LinkedList<>();
    private final Hashtable<String, EPCBean> multiBankAutoReadEpcBeanMap = new Hashtable<>() ;
    private MultiBankAutoReadTagListViewAdapter multiBankAutoEpcListViewAdapter;
    private CheckBox chkMultiBankAutoReadReserve;
    private EditText edtMultiBankAutoReadReserveOffset;
    private EditText edtMultiBankAutoReadReserveLength;
    private CheckBox chkMultiBankAutoReadTid;
    private EditText edtMultiBankAutoReadTidOffset;
    private EditText edtMultiBankAutoReadTidLength;
    private CheckBox chkMultiBankAutoReadUser;
    private EditText edtMultiBankAutoReadUserOffset;
    private EditText edtMultiBankAutoReadUserLength;
    private EditText edtMultiBankAutoReadAccessPassword;
    private Button btnStartMultiBankAutoRead;
    private Button btnStopMultiBankAutoRead;

    private volatile boolean multiBankAutoReading = false;
    // 多区域盘点}

    private final int[] device_control_layouts = { R.id.tab_radio_params_page, R.id.tab_device_params_page,
            R.id.tab_communication_config_page, R.id.tab_io_control_page
    };
    private static final String[] device_control_layout_titles = { "射频参数", "设备参数", "通讯配置", "I/O控制" };

    // { 射频参数
    private static final int BACKWARDPOWERTHRESHOLD = 6;
    private final Hashtable<Integer, Integer> dwellTimes = new Hashtable<>();
    private final Hashtable<Integer, Integer> outputPowers = new Hashtable<>();
    private final LinearLayout[] llAntConfigs = new LinearLayout[32];
    private int ant_num = 1;
    private final CheckBox[] chkAntEnableGroup = new CheckBox[32];
    private final Spinner[] spinnerAntDwellTimeGroup = new Spinner[32];
    private final Spinner[] spinnerAntOutputPowerGroup = new Spinner[32];
    private final TextView[] tvAntReversePowerDifferenceGroup = new TextView[32];
    private CheckBox chkAntEchoDetectingEnable;
    private final byte[] ant_config = new byte[12 * 48];
    private final int[] ant_config_len = new int[]{ant_config.length};

    private EditText edtMacRegisterAddress;
    private EditText edtMacRegisterValue;
    private EditText edtOemRegisterAddress;
    private EditText edtOemRegisterValue;

    private Spinner spinnerLinkFrequency;

    private EditText edtFrequencyBandFilePath;
    private Uri frequencyBandFileUri = null;

    private EditText edtTxOnTime;
    private CheckBox chkTxOffTimeEnable;
    private EditText edtTxOffTime;

    private Spinner spinnerQValueMode;
    private EditText edtStartQValue;
    private EditText edtMaxQValue;
    private EditText edtMinQValue;
    private EditText edtThresholdMultiplier;

    private Spinner spinnerQueryTarget;
    private Spinner spinnerQuerySession;
    private Spinner spinnerQuerySelect;

    private EditText edtInventoryConfigAlgorithm;
    private EditText edtInventoryConfigMatchReport;
    private CheckBox chkInventoryConfigAutoSelect;
    private CheckBox chkInventoryConfigDisableInventory;

    private EditText edtCarrierAdjustTime;

    private Spinner spinnerHoppingChannel;
    private final ArrayList<String> hoppingChannelList = new ArrayList<>();
    private ArrayAdapter<String> hoppingListAdapter;
    private Spinner spinnerTestAnt;
    private final ArrayList<Integer> testAntList = new ArrayList<>();
    private ArrayAdapter<Integer> testAntListAdapter;
    private Spinner spinnerTestPower;
    private Spinner spinnerTestFrequency;
    private final ArrayList<String> testFrequencyList = new ArrayList<>();
    private ArrayAdapter<String> testFrequencyListAdapter;
    private String transmitFrequencyStandard = "Unknown";
    private JSONObject channelNumberToFrequencyCfg = null;
    // 射频参数 }

    // { 设备参数
    private Spinner spinnerWorkMode;

    private EditText edtTriggerModeDelayStopInventoryTime;

    private Spinner spinnerAutoReadBank;
    private EditText edtAutoReadPassword;
    private EditText edtAutoReadOffset;
    private EditText edtAutoReadLength;

    private TextView tvRealtimeRtcTime;
    private Timer timerForCurrentTime;
    private EditText edtRtcTime;

    private Spinner spinnerCommunicationMode;

    private CheckBox chkRssiFilterEnable;
    private EditText edtRssiFilterValue;

    private CheckBox chkTagFilterEnable;
    private EditText edtTagFilterTime;

    private CheckBox chkBuzzerEnable;
    // 设备参数 }

    // { 通讯配置
    private Spinner spinnerBaud;
    private EditText edtRs485Address;

    private CheckBox chkWifiIPItem;
    private Spinner spinnerWifiIPMode;
    private EditText edtWifiIP;
    private EditText edtWifiIPMask;
    private EditText edtWifiIPGateway;
    private EditText edtWifiIPDns;
    private CheckBox chkWifiAPItem;
    private EditText edtWifiAPSsid;
    private EditText edtWifiAPKey;
    private CheckBox chkWifiSocketItem;
    private Spinner spinnerWifiSocketMode;
    private EditText edtWifiSocketIP;
    private EditText edtWifiSocketPort;
    private CheckBox chkWifiMacItem;
    private EditText edtWifiMac;

    private CheckBox chkGprsSocketItem;
    private Spinner spinnerGprsSocketProtocol;
    private EditText edtGprsSocketIP;
    private EditText edtGprsSocketPort;
    private CheckBox chkGprsAPNItem;
    private EditText edtGprsApn;
    private Spinner spinnerGprsAuth;
    private EditText edtGprsApnAccount;
    private EditText edtGprsApnPassword;

    private Spinner spinnerWgProtocolFormat;
    private EditText edtWgDataStartAddress;
    private EditText edtWgPulseWidth;
    private EditText edtWgPulseCycle;
    private EditText edtWgDataInterval;
    // 通讯配置 }

    // { I/O控制
    private CheckBox chkIoInput1State;

    private CheckBox chkRelay1State;
    private CheckBox chkRelay2State;
    // I/O控制 }

    // { 系统配置
    private EditText edtTransmitFrequencyStandard;
    private EditText edtFirmwareVersion;
    private EditText edtDeviceName;
    private EditText edtUpgradeFilePath;
    private Uri upgradeFileUri = null;

    private EditText edtAllSystemParams;

    private EditText edtSN;

    private EditText edtHeartbeatInterval;

    private EditText[] edtMacAddressGroup;
    private Spinner spinnerProductType;
    // 系统配置 }

    // { 上报
    private EditText edtReport;
    //  上报 }

    private Dialog dialog = null;
    private TextView tvMessage = null;

    private static final int REQUEST_CODE_FOR_SEARCH_READER = 0x01;
    private static final int REQUEST_CODE_FOR_FREQUENCY_FILE = 0x02;
    private static final int REQUEST_CODE_FOR_UPGRADE_FILE = 0x03;


    private static final Hashtable<Integer, Integer> BAUD_INDEX_MAP = new Hashtable<>();
    static {
        BAUD_INDEX_MAP.put(230400, 0);
        BAUD_INDEX_MAP.put(115200, 1);
        BAUD_INDEX_MAP.put(57600, 2);
        BAUD_INDEX_MAP.put(38400, 3);
        BAUD_INDEX_MAP.put(19200, 4);
        BAUD_INDEX_MAP.put(9600, 5);
        BAUD_INDEX_MAP.put(4800, 6);
    }

    private EraLinkUHFReaderInterface uhfReaderInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 应用运行时，保持屏幕高亮，不锁屏
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeButtonEnabled(true);
        }

        { // { 主界面
            TabHost main_tabHost = findViewById(R.id.activity_main);
            main_tabHost.setup();
            for (int i = 0; i < main_layouts.length; ++i) {
                TabHost.TabSpec tabSpec = main_tabHost.newTabSpec(Integer.toString(i));
                tabSpec.setIndicator(main_layout_titles[i]);
                tabSpec.setContent(main_layouts[i]);
                main_tabHost.addTab(tabSpec);
            }
            mainTabWidget = main_tabHost.getTabWidget();
            for (int i = 0; i < mainTabWidget.getChildCount(); ++i) {
                TextView tv = mainTabWidget.getChildAt(i).findViewById(android.R.id.title);
                tv.setTransformationMethod(null);
            }
            main_tabHost.setCurrentTab(0);
            main_tabHost.setOnTabChangedListener(tabId -> {
                stopInventoryAction();
                stopAutoReadAction();
                stopMultiBankAutoReadAction();
            });
        } // 主界面

        { // 读写器

            spinnerReaderWokeRole = findViewById(R.id.spinner_reader_woke_role);
            llReaderAsServer = findViewById(R.id.ll_reader_as_server);
            llReaderAsClient = findViewById(R.id.ll_reader_as_client);
            spinnerReaderWokeRole.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    llReaderAsServer.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
                    llReaderAsClient.setVisibility(position == 1 ? View.VISIBLE : View.GONE);
                    reader_work_role = position;
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
            spinnerReaderConnectMode = findViewById(R.id.spinner_reader_connect_mode);
            llUartCommunicationGroup = findViewById(R.id.ll_uart_communication_group);
            llNetworkCommunicationGroup = findViewById(R.id.ll_net_communication_group);
            spinnerReaderConnectMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    llUartCommunicationGroup.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
                    llNetworkCommunicationGroup.setVisibility(position == 1 ? View.VISIBLE : View.GONE);
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
            edtReaderIP = findViewById(R.id.edt_reader_ip);
            edtReaderPort = findViewById(R.id.edt_reader_port);
            spinnerUart = findViewById(R.id.spinner_uart);
            spinnerConnectBaud = findViewById(R.id.spinner_connect_baud);
            spinnerConnectBaud.setSelection(1);

            String[] uartNames = new SerialPortFinder().getAllDevicesPath();
            ArrayList<CharSequence> uartNameList = new ArrayList<>();
            Collections.addAll(uartNameList, uartNames);
            ArrayAdapter<CharSequence> uartNamesAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, uartNameList);
            spinnerUart.setAdapter(uartNamesAdapter);

            btnSearchReader = findViewById(R.id.btn_search_reader);
            btnSearchReader.setOnClickListener(v -> searchReaderAction());

            btnConnectReader = findViewById(R.id.btn_connect_reader);
            btnDisconnectReader = findViewById(R.id.btn_disconnect_reader);
            btnConnectReader.setOnClickListener(v -> connectReaderAction());
            btnDisconnectReader.setOnClickListener(v -> disconnectReaderAction());

            edtListenIP = findViewById(R.id.edt_listen_ip);
            edtListenPort = findViewById(R.id.edt_listen_port);
            btnStartListen = findViewById(R.id.btn_start_listen);
            btnStopListen = findViewById(R.id.btn_stop_listen);
            btnStartListen.setOnClickListener(v -> startListenAction());
            btnStopListen.setOnClickListener(v -> stopListenAction());
            readerListViewAdapter = new ReaderListViewAdapter(this, readerList);
            ListView lvReader = findViewById(R.id.lv_reader);
            lvReader.setAdapter(readerListViewAdapter);
            lvReader.setOnItemClickListener((parent, view, position, id) -> {
                reader_id = readerList.get(position).getReaderId();
                readerListViewAdapter.setSelectedItemId(position);
                readerListViewAdapter.notifyDataSetChanged();
            });

            btnRemoveReader = findViewById(R.id.btn_remove_reader);
            btnRemoveReader.setOnClickListener(v -> removeReaderAction());
        } // 读写器

        { // 标签
            TabHost tag_tabHost = findViewById(R.id.tab_tag_page);
            tag_tabHost.setup();
            for (int i = 0; i < tag_layouts.length; ++i) {
                TabHost.TabSpec tabSpec = tag_tabHost.newTabSpec(Integer.toString(i));
                tabSpec.setIndicator(tag_layout_titles[i]);
                tabSpec.setContent(tag_layouts[i]);
                tag_tabHost.addTab(tabSpec);
            }
            tagTabWidget = tag_tabHost.getTabWidget();
            for (int i = 0; i < tagTabWidget.getChildCount(); ++i) {
                TextView tv = tagTabWidget.getChildAt(i).findViewById(android.R.id.title);
                tv.setTransformationMethod(null);
            }
            tag_tabHost.setCurrentTab(0);
        } // 标签

        { // { 读标签
            TabHost tabHost = findViewById(R.id.tab_read_tag_page);
            tabHost.setup();
            for (int i = 0; i < read_tag_layouts.length; ++i) {
                TabHost.TabSpec tabSpec = tabHost.newTabSpec(Integer.toString(i));
                tabSpec.setIndicator(read_tag_layout_titles[i]);
                tabSpec.setContent(read_tag_layouts[i]);
                tabHost.addTab(tabSpec);
            }
            readTagTabWidget = tabHost.getTabWidget();
            for (int i = 0; i < readTagTabWidget.getChildCount(); ++i) {
                TextView tv = readTagTabWidget.getChildAt(i).findViewById(android.R.id.title);
                tv.setTransformationMethod(null);
            }
            tabHost.setCurrentTab(0);
        } // 读标签 }

        { // { 标签盘点
            tvInventoryElapsedTime = findViewById(R.id.tv_inventory_elapsed_time);
            tvInventoryRate = findViewById(R.id.tv_inventory_rate);
            tvInventoryTagNumber = findViewById(R.id.tv_inventory_tag_number);
            tvInventoryTagReportTotalNumber = findViewById(R.id.tv_inventory_tag_report_total_number);

            ListView lvTag = findViewById(R.id.lv_tag);
            epcListViewAdapter = new InventoryTagListViewAdapter(this, epcBeanList);
            lvTag.setAdapter(epcListViewAdapter);

            btnInventoryOnce = findViewById(R.id.btn_inventory_once);
            btnInventory = findViewById(R.id.btn_inventory);
            btnStopInventory = findViewById(R.id.btn_stop_inventory);
            btnInventoryOnce.setOnClickListener(v -> inventoryOnceAction());
            btnInventory.setOnClickListener(v -> inventoryAction());
            btnStopInventory.setOnClickListener(v -> stopInventoryAction());
            findViewById(R.id.btn_clear_inventory).setOnClickListener(v -> clearInventoryAction());

            tvInventoryStatisticsDuration = findViewById(R.id.tv_inventory_statistics_duration);
            tvInventoryStatisticsQueryCount = findViewById(R.id.tv_inventory_statistics_query_count);
            tvInventoryStatisticsReceivedRN16Count = findViewById(R.id.tv_inventory_statistics_receive_rn16_count);
            tvInventoryStatisticsRN16TimeoutCount = findViewById(R.id.tv_inventory_statistics_rn16_timeout_count);
            tvInventoryStatisticsEPCTimeoutCount = findViewById(R.id.tv_inventory_statistics_epc_timeout_count);
            tvInventoryStatisticsTagReadCount = findViewById(R.id.tv_inventory_statistics_tag_read_count);
            tvInventoryStatisticsEPCCrcCount = findViewById(R.id.tv_inventory_statistics_epc_crc_count);
            tvInventoryStatisticsInterfererBlockingDuration = findViewById(R.id.tv_inventory_statistics_interferer_blocking_duration);
            getEdtInventoryStatisticsInterfererBlockingCount = findViewById(R.id.tv_inventory_statistics_interferer_blocking_count);
            btnGetInventoryStatistics = findViewById(R.id.btn_get_inventory_statistics);
            btnGetInventoryStatistics.setOnClickListener(v -> getInventoryStatisticsAction());
        } // 标签盘点 }

        { // { 自动读卡
            tvAutoReadElapsedTime = findViewById(R.id.tv_auto_read_elapsed_time);
            tvAutoReadRate = findViewById(R.id.tv_auto_read_rate);
            tvAutoReadTagNumber = findViewById(R.id.tv_auto_read_tag_number);
            tvAutoReadTagReportTotalNumber = findViewById(R.id.tv_auto_read_tag_report_total_number);

            tvAutoReadPcHead = findViewById(R.id.tv_auto_read_pc_head);
            tvAutoReadEpcHead = findViewById(R.id.tv_auto_read_epc_head);
            tvAutoReadRssiHead = findViewById(R.id.tv_auto_read_rssi_head);
            tvAutoReadTidHead = findViewById(R.id.tv_auto_read_tid_head);
            tvAutoReadUserHead = findViewById(R.id.tv_auto_read_user_head);

            ListView lvAutoReadTag = findViewById(R.id.lv_auto_read_tag);
            autoReadEpcListViewAdapter = new AutoReadTagListViewAdapter(this, autoReadEpcBeanList);
            lvAutoReadTag.setAdapter(autoReadEpcListViewAdapter);

            btnStartAutoRead = findViewById(R.id.btn_start_auto_read);
            btnStopAutoRead = findViewById(R.id.btn_stop_auto_read);
            btnStartAutoRead.setOnClickListener(v -> startAutoReadAction());
            btnStopAutoRead.setOnClickListener(v -> stopAutoReadAction());
            findViewById(R.id.btn_clear_auto_read).setOnClickListener(v -> clearAutoReadAction());
        } // 自动读卡 }

        { // 标签操作
            edtTagAccessPassword = findViewById(R.id.edt_tag_access_password);
            spinnerTagAccessBank = findViewById(R.id.spinner_tag_access_bank);
            edtTagAccessOffset = findViewById(R.id.edt_tag_access_offset);
            edtTagAccessLength = findViewById(R.id.edt_tag_access_length);
            edtTagData = findViewById(R.id.edt_tag_data);
            findViewById(R.id.btn_read_tag).setOnClickListener(v -> readTagAction());
            findViewById(R.id.btn_write_tag).setOnClickListener(v -> writeTagAction());
            findViewById(R.id.btn_tag_block_write).setOnClickListener(v -> tagBlockWriteAction());
            findViewById(R.id.btn_erase_tag_block).setOnClickListener(v -> tagBlockEraseAction());

            spinnerTagLockType = findViewById(R.id.spinner_tag_lock_type);
            spinnerTagLockBank = findViewById(R.id.spinner_tag_lock_bank);
            findViewById(R.id.btn_lock_tag).setOnClickListener(v -> lockTagAction());

            edtTagKillPassword = findViewById(R.id.edt_tag_kill_password);
            findViewById(R.id.btn_kill_tag).setOnClickListener(v -> killTagAction());

            spinnerTagSelectBank = findViewById(R.id.spinner_tag_select_bank);
            edtTagSelectOffset = findViewById(R.id.edt_tag_select_offset);
            edtTagSelectLength = findViewById(R.id.edt_tag_select_length);
            edtTagSelectData = findViewById(R.id.edt_tag_select_data);
            findViewById(R.id.btn_get_tag_select).setOnClickListener(v -> getTagSelectAction());
            findViewById(R.id.btn_tag_select).setOnClickListener(v -> tagSelectAction());
            findViewById(R.id.btn_cancel_tag_select).setOnClickListener(v -> cancelTagSelectAction());
        } // 标签操作 }

        { // 多区域盘点
            ListView lvMultiBankAutoReadTag = findViewById(R.id.lv_multi_bank_auto_read_tag);
            multiBankAutoEpcListViewAdapter = new MultiBankAutoReadTagListViewAdapter(this, multiBankAutoReadEpcBeanList);
            lvMultiBankAutoReadTag.setAdapter(multiBankAutoEpcListViewAdapter);
            chkMultiBankAutoReadReserve = findViewById(R.id.chk_multi_bank_auto_read_reserve);
            chkMultiBankAutoReadTid = findViewById(R.id.chk_multi_bank_auto_read_tid);
            chkMultiBankAutoReadUser = findViewById(R.id.chk_multi_bank_auto_read_user);
            edtMultiBankAutoReadReserveOffset = findViewById(R.id.edt_multi_bank_auto_read_reserve_offset);
            edtMultiBankAutoReadReserveLength = findViewById(R.id.edt_multi_bank_auto_read_reserve_length);
            edtMultiBankAutoReadTidOffset = findViewById(R.id.edt_multi_bank_auto_read_tid_offset);
            edtMultiBankAutoReadTidLength = findViewById(R.id.edt_multi_bank_auto_read_tid_length);
            edtMultiBankAutoReadUserOffset = findViewById(R.id.edt_multi_bank_auto_read_user_offset);
            edtMultiBankAutoReadUserLength = findViewById(R.id.edt_multi_bank_auto_read_user_length);
            edtMultiBankAutoReadAccessPassword = findViewById(R.id.edt_multi_bank_auto_read_access_password);
            chkMultiBankAutoReadReserve.setOnCheckedChangeListener((buttonView, isChecked) -> {
                findViewById(R.id.tv_multi_bank_auto_read_reserve_head).setVisibility(isChecked ? View.VISIBLE : View.GONE);
                multiBankAutoEpcListViewAdapter.setHideReserveColumn(!isChecked);
            });
            chkMultiBankAutoReadTid.setOnCheckedChangeListener((buttonView, isChecked) -> {
                findViewById(R.id.tv_multi_bank_auto_read_tid_head).setVisibility(isChecked ? View.VISIBLE : View.GONE);
                multiBankAutoEpcListViewAdapter.setHideTidColumn(!isChecked);
            });
            chkMultiBankAutoReadUser.setOnCheckedChangeListener((buttonView, isChecked) -> {
                findViewById(R.id.tv_multi_bank_auto_read_user_head).setVisibility(isChecked ? View.VISIBLE : View.GONE);
                multiBankAutoEpcListViewAdapter.setHideUserColumn(!isChecked);
            });
            btnStartMultiBankAutoRead = findViewById(R.id.btn_start_multi_bank_auto_read);
            btnStopMultiBankAutoRead = findViewById(R.id.btn_stop_multi_bank_auto_read);
            btnStartMultiBankAutoRead.setOnClickListener(v -> startMultiBankAutoReadAction());
            btnStopMultiBankAutoRead.setOnClickListener(v -> stopMultiBankAutoReadAction());
            findViewById(R.id.btn_clear_multi_bank_auto_read).setOnClickListener(v -> clearMultiBankAutoReadAction());
        } // 多区域盘点

        { // 设备控制
            TabHost device_control_tabHost = findViewById(R.id.tab_device_control_page);
            device_control_tabHost.setup();
            for (int i = 0; i < device_control_layouts.length; ++i) {
                TabHost.TabSpec tabSpec = device_control_tabHost.newTabSpec(Integer.toString(i));
                tabSpec.setIndicator(device_control_layout_titles[i]);
                tabSpec.setContent(device_control_layouts[i]);
                device_control_tabHost.addTab(tabSpec);
            }
            TabWidget tabWidget = device_control_tabHost.getTabWidget();
            for (int i = 0; i < tabWidget.getChildCount(); ++i) {
                TextView tv = tabWidget.getChildAt(i).findViewById(android.R.id.title);
                tv.setTransformationMethod(null);
            }
            device_control_tabHost.setCurrentTab(0);
        } // 设备控制

        { // 射频参数
            {
                dwellTimes.put(3000, 0);
                dwellTimes.put(2000, 1);
                dwellTimes.put(1500, 2);
                dwellTimes.put(1000, 3);
                dwellTimes.put(500, 4);
                dwellTimes.put(300, 5);
                dwellTimes.put(200, 6);
                dwellTimes.put(100, 7);
                int powerIndex = 0;
                for (int power = 33; power >= 5; --power) {
                    outputPowers.put(power, powerIndex++);
                }
            }

            {
                llAntConfigs[0] = findViewById(R.id.ll_ant1_config);
                llAntConfigs[1] = findViewById(R.id.ll_ant2_config);
                llAntConfigs[2] = findViewById(R.id.ll_ant3_config);
                llAntConfigs[3] = findViewById(R.id.ll_ant4_config);
                llAntConfigs[4] = findViewById(R.id.ll_ant5_config);
                llAntConfigs[5] = findViewById(R.id.ll_ant6_config);
                llAntConfigs[6] = findViewById(R.id.ll_ant7_config);
                llAntConfigs[7] = findViewById(R.id.ll_ant8_config);
                llAntConfigs[8] = findViewById(R.id.ll_ant9_config);
                llAntConfigs[9] = findViewById(R.id.ll_ant10_config);
                llAntConfigs[10] = findViewById(R.id.ll_ant11_config);
                llAntConfigs[11] = findViewById(R.id.ll_ant12_config);
                llAntConfigs[12] = findViewById(R.id.ll_ant13_config);
                llAntConfigs[13] = findViewById(R.id.ll_ant14_config);
                llAntConfigs[14] = findViewById(R.id.ll_ant15_config);
                llAntConfigs[15] = findViewById(R.id.ll_ant16_config);
                llAntConfigs[16] = findViewById(R.id.ll_ant17_config);
                llAntConfigs[17] = findViewById(R.id.ll_ant18_config);
                llAntConfigs[18] = findViewById(R.id.ll_ant19_config);
                llAntConfigs[19] = findViewById(R.id.ll_ant20_config);
                llAntConfigs[20] = findViewById(R.id.ll_ant21_config);
                llAntConfigs[21] = findViewById(R.id.ll_ant22_config);
                llAntConfigs[22] = findViewById(R.id.ll_ant23_config);
                llAntConfigs[23] = findViewById(R.id.ll_ant24_config);
                llAntConfigs[24] = findViewById(R.id.ll_ant25_config);
                llAntConfigs[25] = findViewById(R.id.ll_ant26_config);
                llAntConfigs[26] = findViewById(R.id.ll_ant27_config);
                llAntConfigs[27] = findViewById(R.id.ll_ant28_config);
                llAntConfigs[28] = findViewById(R.id.ll_ant29_config);
                llAntConfigs[29] = findViewById(R.id.ll_ant30_config);
                llAntConfigs[30] = findViewById(R.id.ll_ant31_config);
                llAntConfigs[31] = findViewById(R.id.ll_ant32_config);
            }

            {
                chkAntEnableGroup[0] = findViewById(R.id.chk_ant1);
                chkAntEnableGroup[1] = findViewById(R.id.chk_ant2);
                chkAntEnableGroup[2] = findViewById(R.id.chk_ant3);
                chkAntEnableGroup[3] = findViewById(R.id.chk_ant4);
                chkAntEnableGroup[4] = findViewById(R.id.chk_ant5);
                chkAntEnableGroup[5] = findViewById(R.id.chk_ant6);
                chkAntEnableGroup[6] = findViewById(R.id.chk_ant7);
                chkAntEnableGroup[7] = findViewById(R.id.chk_ant8);
                chkAntEnableGroup[8] = findViewById(R.id.chk_ant9);
                chkAntEnableGroup[9] = findViewById(R.id.chk_ant10);
                chkAntEnableGroup[10] = findViewById(R.id.chk_ant11);
                chkAntEnableGroup[11] = findViewById(R.id.chk_ant12);
                chkAntEnableGroup[12] = findViewById(R.id.chk_ant13);
                chkAntEnableGroup[13] = findViewById(R.id.chk_ant14);
                chkAntEnableGroup[14] = findViewById(R.id.chk_ant15);
                chkAntEnableGroup[15] = findViewById(R.id.chk_ant16);
                chkAntEnableGroup[16] = findViewById(R.id.chk_ant17);
                chkAntEnableGroup[17] = findViewById(R.id.chk_ant18);
                chkAntEnableGroup[18] = findViewById(R.id.chk_ant19);
                chkAntEnableGroup[19] = findViewById(R.id.chk_ant20);
                chkAntEnableGroup[20] = findViewById(R.id.chk_ant21);
                chkAntEnableGroup[21] = findViewById(R.id.chk_ant22);
                chkAntEnableGroup[22] = findViewById(R.id.chk_ant23);
                chkAntEnableGroup[23] = findViewById(R.id.chk_ant24);
                chkAntEnableGroup[24] = findViewById(R.id.chk_ant25);
                chkAntEnableGroup[25] = findViewById(R.id.chk_ant26);
                chkAntEnableGroup[26] = findViewById(R.id.chk_ant27);
                chkAntEnableGroup[27] = findViewById(R.id.chk_ant28);
                chkAntEnableGroup[28] = findViewById(R.id.chk_ant29);
                chkAntEnableGroup[29] = findViewById(R.id.chk_ant30);
                chkAntEnableGroup[30] = findViewById(R.id.chk_ant31);
                chkAntEnableGroup[31] = findViewById(R.id.chk_ant32);
            }

            {
                spinnerAntDwellTimeGroup[0] = findViewById(R.id.spinner_ant1_dwell_time);
                spinnerAntDwellTimeGroup[1] = findViewById(R.id.spinner_ant2_dwell_time);
                spinnerAntDwellTimeGroup[2] = findViewById(R.id.spinner_ant3_dwell_time);
                spinnerAntDwellTimeGroup[3] = findViewById(R.id.spinner_ant4_dwell_time);
                spinnerAntDwellTimeGroup[4] = findViewById(R.id.spinner_ant5_dwell_time);
                spinnerAntDwellTimeGroup[5] = findViewById(R.id.spinner_ant6_dwell_time);
                spinnerAntDwellTimeGroup[6] = findViewById(R.id.spinner_ant7_dwell_time);
                spinnerAntDwellTimeGroup[7] = findViewById(R.id.spinner_ant8_dwell_time);
                spinnerAntDwellTimeGroup[8] = findViewById(R.id.spinner_ant9_dwell_time);
                spinnerAntDwellTimeGroup[9] = findViewById(R.id.spinner_ant10_dwell_time);
                spinnerAntDwellTimeGroup[10] = findViewById(R.id.spinner_ant11_dwell_time);
                spinnerAntDwellTimeGroup[11] = findViewById(R.id.spinner_ant12_dwell_time);
                spinnerAntDwellTimeGroup[12] = findViewById(R.id.spinner_ant13_dwell_time);
                spinnerAntDwellTimeGroup[13] = findViewById(R.id.spinner_ant14_dwell_time);
                spinnerAntDwellTimeGroup[14] = findViewById(R.id.spinner_ant15_dwell_time);
                spinnerAntDwellTimeGroup[15] = findViewById(R.id.spinner_ant16_dwell_time);
                spinnerAntDwellTimeGroup[16] = findViewById(R.id.spinner_ant17_dwell_time);
                spinnerAntDwellTimeGroup[17] = findViewById(R.id.spinner_ant18_dwell_time);
                spinnerAntDwellTimeGroup[18] = findViewById(R.id.spinner_ant19_dwell_time);
                spinnerAntDwellTimeGroup[19] = findViewById(R.id.spinner_ant20_dwell_time);
                spinnerAntDwellTimeGroup[20] = findViewById(R.id.spinner_ant21_dwell_time);
                spinnerAntDwellTimeGroup[21] = findViewById(R.id.spinner_ant22_dwell_time);
                spinnerAntDwellTimeGroup[22] = findViewById(R.id.spinner_ant23_dwell_time);
                spinnerAntDwellTimeGroup[23] = findViewById(R.id.spinner_ant24_dwell_time);
                spinnerAntDwellTimeGroup[24] = findViewById(R.id.spinner_ant25_dwell_time);
                spinnerAntDwellTimeGroup[25] = findViewById(R.id.spinner_ant26_dwell_time);
                spinnerAntDwellTimeGroup[26] = findViewById(R.id.spinner_ant27_dwell_time);
                spinnerAntDwellTimeGroup[27] = findViewById(R.id.spinner_ant28_dwell_time);
                spinnerAntDwellTimeGroup[28] = findViewById(R.id.spinner_ant29_dwell_time);
                spinnerAntDwellTimeGroup[29] = findViewById(R.id.spinner_ant30_dwell_time);
                spinnerAntDwellTimeGroup[30] = findViewById(R.id.spinner_ant31_dwell_time);
                spinnerAntDwellTimeGroup[31] = findViewById(R.id.spinner_ant32_dwell_time);
            }

            {
                spinnerAntOutputPowerGroup[0] = findViewById(R.id.spinner_ant1_power);
                spinnerAntOutputPowerGroup[1] = findViewById(R.id.spinner_ant2_power);
                spinnerAntOutputPowerGroup[2] = findViewById(R.id.spinner_ant3_power);
                spinnerAntOutputPowerGroup[3] = findViewById(R.id.spinner_ant4_power);
                spinnerAntOutputPowerGroup[4] = findViewById(R.id.spinner_ant5_power);
                spinnerAntOutputPowerGroup[5] = findViewById(R.id.spinner_ant6_power);
                spinnerAntOutputPowerGroup[6] = findViewById(R.id.spinner_ant7_power);
                spinnerAntOutputPowerGroup[7] = findViewById(R.id.spinner_ant8_power);
                spinnerAntOutputPowerGroup[8] = findViewById(R.id.spinner_ant9_power);
                spinnerAntOutputPowerGroup[9] = findViewById(R.id.spinner_ant10_power);
                spinnerAntOutputPowerGroup[10] = findViewById(R.id.spinner_ant11_power);
                spinnerAntOutputPowerGroup[11] = findViewById(R.id.spinner_ant12_power);
                spinnerAntOutputPowerGroup[12] = findViewById(R.id.spinner_ant13_power);
                spinnerAntOutputPowerGroup[13] = findViewById(R.id.spinner_ant14_power);
                spinnerAntOutputPowerGroup[14] = findViewById(R.id.spinner_ant15_power);
                spinnerAntOutputPowerGroup[15] = findViewById(R.id.spinner_ant16_power);
                spinnerAntOutputPowerGroup[16] = findViewById(R.id.spinner_ant17_power);
                spinnerAntOutputPowerGroup[17] = findViewById(R.id.spinner_ant18_power);
                spinnerAntOutputPowerGroup[18] = findViewById(R.id.spinner_ant19_power);
                spinnerAntOutputPowerGroup[19] = findViewById(R.id.spinner_ant20_power);
                spinnerAntOutputPowerGroup[20] = findViewById(R.id.spinner_ant21_power);
                spinnerAntOutputPowerGroup[21] = findViewById(R.id.spinner_ant22_power);
                spinnerAntOutputPowerGroup[22] = findViewById(R.id.spinner_ant23_power);
                spinnerAntOutputPowerGroup[23] = findViewById(R.id.spinner_ant24_power);
                spinnerAntOutputPowerGroup[24] = findViewById(R.id.spinner_ant25_power);
                spinnerAntOutputPowerGroup[25] = findViewById(R.id.spinner_ant26_power);
                spinnerAntOutputPowerGroup[26] = findViewById(R.id.spinner_ant27_power);
                spinnerAntOutputPowerGroup[27] = findViewById(R.id.spinner_ant28_power);
                spinnerAntOutputPowerGroup[28] = findViewById(R.id.spinner_ant29_power);
                spinnerAntOutputPowerGroup[29] = findViewById(R.id.spinner_ant30_power);
                spinnerAntOutputPowerGroup[30] = findViewById(R.id.spinner_ant31_power);
                spinnerAntOutputPowerGroup[31] = findViewById(R.id.spinner_ant32_power);
            }

            {
                tvAntReversePowerDifferenceGroup[0] = findViewById(R.id.tv_ant1_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[1] = findViewById(R.id.tv_ant2_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[2] = findViewById(R.id.tv_ant3_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[3] = findViewById(R.id.tv_ant4_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[4] = findViewById(R.id.tv_ant5_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[5] = findViewById(R.id.tv_ant6_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[6] = findViewById(R.id.tv_ant7_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[7] = findViewById(R.id.tv_ant8_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[8] = findViewById(R.id.tv_ant9_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[9] = findViewById(R.id.tv_ant10_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[10] = findViewById(R.id.tv_ant11_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[11] = findViewById(R.id.tv_ant12_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[12] = findViewById(R.id.tv_ant13_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[13] = findViewById(R.id.tv_ant14_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[14] = findViewById(R.id.tv_ant15_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[15] = findViewById(R.id.tv_ant16_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[16] = findViewById(R.id.tv_ant17_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[17] = findViewById(R.id.tv_ant18_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[18] = findViewById(R.id.tv_ant19_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[19] = findViewById(R.id.tv_ant20_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[20] = findViewById(R.id.tv_ant21_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[21] = findViewById(R.id.tv_ant22_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[22] = findViewById(R.id.tv_ant23_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[23] = findViewById(R.id.tv_ant24_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[24] = findViewById(R.id.tv_ant25_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[25] = findViewById(R.id.tv_ant26_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[26] = findViewById(R.id.tv_ant27_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[27] = findViewById(R.id.tv_ant28_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[28] = findViewById(R.id.tv_ant29_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[29] = findViewById(R.id.tv_ant30_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[30] = findViewById(R.id.tv_ant31_reverse_power_difference);
                tvAntReversePowerDifferenceGroup[31] = findViewById(R.id.tv_ant32_reverse_power_difference);
            }

            findViewById(R.id.btn_refresh_ant_config).setOnClickListener(v -> refreshAntConfigAction());
            findViewById(R.id.btn_get_ant_config).setOnClickListener(v -> getAntConfigAction());
            findViewById(R.id.btn_set_ant_config).setOnClickListener(v -> setAntConfigAction());
            findViewById(R.id.btn_clear_ant_config).setOnClickListener(v -> clearAntConfigAction());

            chkAntEchoDetectingEnable = findViewById(R.id.chk_ant_echo_detecting_enable);
            chkAntEchoDetectingEnable.setOnCheckedChangeListener((v, isChecked) -> antEchoDetectingEnableAction());

            edtMacRegisterAddress = findViewById(R.id.edt_mac_register_address);
            edtMacRegisterValue = findViewById(R.id.edt_mac_register_value);
            edtOemRegisterAddress = findViewById(R.id.edt_oem_register_address);
            edtOemRegisterValue = findViewById(R.id.edt_oem_register_value);
            findViewById(R.id.btn_read_mac_register).setOnClickListener(v -> readMacRegisterAction());
            findViewById(R.id.btn_write_mac_register).setOnClickListener(v -> writeMacRegisterAction());
            findViewById(R.id.btn_read_oem_register).setOnClickListener(v -> readOemRegisterAction());
            findViewById(R.id.btn_write_oem_register).setOnClickListener(v -> writeOemRegisterAction());

            spinnerLinkFrequency = findViewById(R.id.spinner_link_frequency);
            findViewById(R.id.btn_get_link_frequency).setOnClickListener(v -> getLinkFrequencyAction());
            findViewById(R.id.btn_set_link_frequency).setOnClickListener(v -> setLinkFrequencyAction());

            edtFrequencyBandFilePath = findViewById(R.id.edt_frequency_band_file_path);
            findViewById(R.id.btn_select_frequency_band_file).setOnClickListener(v -> selectFrequencyBandFileAction());
            findViewById(R.id.btn_set_frequency_band).setOnClickListener(v -> setFrequencyBandAction());

            edtTxOnTime = findViewById(R.id.edt_tx_on_time);
            chkTxOffTimeEnable = findViewById(R.id.chk_tx_off_time_enable);
            edtTxOffTime = findViewById(R.id.edt_tx_off_time);
            findViewById(R.id.btn_get_tx_on_off_time).setOnClickListener(v -> getTxOnOffTimeAction());
            findViewById(R.id.btn_set_tx_on_off_time).setOnClickListener(v -> setTxOnOffTimeAction());

            spinnerQValueMode = findViewById(R.id.spinner_q_value_mode);
            edtStartQValue = findViewById(R.id.edt_start_q_value);
            edtMaxQValue = findViewById(R.id.edt_max_q_value);
            edtMinQValue = findViewById(R.id.edt_min_q_value);
            edtThresholdMultiplier = findViewById(R.id.edt_threshold_multiplier);
            spinnerQValueMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                    TextView tv_static_q_value = findViewById(R.id.tv_static_q_value);
                    TextView tv_dynamic_start_q_value = findViewById(R.id.tv_dynamic_start_q_value);
                    LinearLayout ll_dynamic_q_value = findViewById(R.id.ll_dynamic_q_value);
                    tv_static_q_value.setVisibility(position == 0 ? View.VISIBLE : View.GONE);
                    tv_dynamic_start_q_value.setVisibility(position == 0 ? View.GONE : View.VISIBLE);
                    ll_dynamic_q_value.setVisibility(position == 0 ? View.GONE : View.VISIBLE);
                }

                @Override
                public void onNothingSelected(AdapterView<?> parent) {

                }
            });
            findViewById(R.id.btn_get_q_value).setOnClickListener(v -> getQValueAction());
            findViewById(R.id.btn_set_q_value).setOnClickListener(v -> setQValueAction());

            spinnerQueryTarget = findViewById(R.id.spinner_query_target);
            spinnerQuerySession = findViewById(R.id.spinner_query_session);
            spinnerQuerySelect = findViewById(R.id.spinner_query_select);
            findViewById(R.id.btn_get_query_config).setOnClickListener(v -> getQueryConfigAction());
            findViewById(R.id.btn_set_query_config).setOnClickListener(v -> setQueryConfigAction());

            edtInventoryConfigAlgorithm = findViewById(R.id.edt_inventory_config_algorithm);
            edtInventoryConfigMatchReport = findViewById(R.id.edt_inventory_config_match_report);
            chkInventoryConfigAutoSelect = findViewById(R.id.chk_inventory_config_auto_select);
            chkInventoryConfigDisableInventory = findViewById(R.id.chk_inventory_config_disable_inventory);
            findViewById(R.id.btn_get_inventory_config).setOnClickListener(v -> getInventoryConfigAction());
            findViewById(R.id.btn_set_inventory_config).setOnClickListener(v -> setInventoryConfigAction());

            edtCarrierAdjustTime = findViewById(R.id.edt_carrier_adjust_time);
            findViewById(R.id.btn_get_carrier_adjust_time).setOnClickListener(v -> getCarrierAdjustTimeAction());
            findViewById(R.id.btn_set_carrier_adjust_time).setOnClickListener(v -> setCarrierAdjustTimeAction());

            spinnerHoppingChannel = findViewById(R.id.spinner_hopping_channel);
            hoppingListAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, hoppingChannelList);
            spinnerHoppingChannel.setAdapter(hoppingListAdapter);
            spinnerTestAnt = findViewById(R.id.spinner_test_ant);
            testAntListAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, testAntList);
            spinnerTestAnt.setAdapter(testAntListAdapter);
            spinnerTestPower = findViewById(R.id.spinner_test_ant_power);
            spinnerTestFrequency = findViewById(R.id.spinner_test_frequency);
            testFrequencyListAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, testFrequencyList);
            spinnerTestFrequency.setAdapter(testFrequencyListAdapter);
            findViewById(R.id.btn_cw_on).setOnClickListener(v -> cwOnAction());
            findViewById(R.id.btn_cw_off).setOnClickListener(v -> cwOffAction());
            findViewById(R.id.btn_hopping_on).setOnClickListener(v -> hoppingOnAction());
            findViewById(R.id.btn_hopping_off).setOnClickListener(v -> hoppingOffAction());
            findViewById(R.id.btn_set_test_ant_power).setOnClickListener(v -> setTestAntPowerAction());
            findViewById(R.id.btn_set_test_frequency).setOnClickListener(v -> setTestFrequencyAction());
        } // 射频参数

        { // { 设备参数
            spinnerWorkMode = findViewById(R.id.spinner_work_mode);
            findViewById(R.id.btn_get_work_mode).setOnClickListener(v -> getWorkModeAction());
            findViewById(R.id.btn_set_work_mode).setOnClickListener(v -> setWorkModeAction());

            edtTriggerModeDelayStopInventoryTime = findViewById(R.id.edt_trigger_mode_delay_stop_inventory_time);
            findViewById(R.id.btn_get_trigger_mode_delay_stop_inventory_time).setOnClickListener(v -> getTriggerModeDelayStopInventoryTimeAction());
            findViewById(R.id.btn_set_trigger_mode_delay_stop_inventory_time).setOnClickListener(v -> setTriggerModeDelayStopInventoryTimeAction());

            spinnerAutoReadBank = findViewById(R.id.spinner_auto_read_bank);
            edtAutoReadPassword = findViewById(R.id.edt_auto_read_password);
            edtAutoReadOffset = findViewById(R.id.edt_auto_read_offset);
            edtAutoReadLength = findViewById(R.id.edt_auto_read_length);
            findViewById(R.id.btn_get_auto_read_params).setOnClickListener(v -> getAutoReadParamsAction());
            findViewById(R.id.btn_set_auto_read_params).setOnClickListener(v -> setAutoReadParamsAction());

            tvRealtimeRtcTime = findViewById(R.id.tv_realtime_rtc_time);
            timerForCurrentTime = new Timer();
            timerForCurrentTime.schedule(new TimerTask() {
                @Override
                public void run() {
                    runOnUiThread(() -> {
                        Calendar calendar = Calendar.getInstance();
                        final int year = calendar.get(Calendar.YEAR);//获取年份
                        final int month = calendar.get(Calendar.MONTH) + 1;//获取月份
                        final int day = calendar.get(Calendar.DATE);//获取日
                        final int hour = calendar.get(Calendar.HOUR_OF_DAY);//小时
                        final int minute = calendar.get(Calendar.MINUTE);//分
                        final int second = calendar.get(Calendar.SECOND);//秒
                        tvRealtimeRtcTime.setText(String.format(Locale.getDefault(), "%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second));
                    });
                }
            }, 0, 1000);
            findViewById(R.id.btn_set_rtc_time).setOnClickListener(v -> setRtcTimeAction());
            edtRtcTime = findViewById(R.id.edt_rtc_time);
            findViewById(R.id.btn_get_rtc_time).setOnClickListener(v -> getRtcTimeAction());

            spinnerCommunicationMode = findViewById(R.id.spinner_communication_mode);
            findViewById(R.id.btn_get_communication_mode).setOnClickListener(v -> getCommunicationModeAction());
            findViewById(R.id.btn_set_communication_mode).setOnClickListener(v -> setCommunicationModeAction());

            chkRssiFilterEnable = findViewById(R.id.chk_rssi_filter_enable);
            edtRssiFilterValue = findViewById(R.id.edt_rssi_filter_value);
            findViewById(R.id.btn_get_rssi_filter).setOnClickListener(v -> getRssiFilterAction());
            findViewById(R.id.btn_set_rssi_filter).setOnClickListener(v -> setRssiFilterAction());

            chkTagFilterEnable = findViewById(R.id.chk_tag_filter_enable);
            edtTagFilterTime = findViewById(R.id.edt_tag_filter_time);
            findViewById(R.id.btn_get_tag_filter).setOnClickListener(v -> getTagFilterAction());
            findViewById(R.id.btn_set_tag_filter).setOnClickListener(v -> setTagFilterAction());

            chkBuzzerEnable = findViewById(R.id.chk_buzzer_enable);
            findViewById(R.id.btn_get_buzzer_enable).setOnClickListener(v -> getBuzzerEnableAction());
            findViewById(R.id.btn_set_buzzer_enable).setOnClickListener(v -> setBuzzerEnableAction());
        } // 设备参数

        { // { 通讯配置
            spinnerBaud = findViewById(R.id.spinner_baud);
            spinnerBaud.setSelection(1);
            findViewById(R.id.btn_get_baud).setOnClickListener(v -> getBaudAction());
            findViewById(R.id.btn_set_baud).setOnClickListener(v -> setBaudAction());

            edtRs485Address = findViewById(R.id.edt_rs485_address);
            findViewById(R.id.btn_get_rs485_address).setOnClickListener(v -> getRs485AddressAction());
            findViewById(R.id.btn_set_rs485_address).setOnClickListener(v -> setRs485AddressAction());

            chkWifiIPItem = findViewById(R.id.chk_wifi_ip_item);
            spinnerWifiIPMode = findViewById(R.id.spinner_wifi_ip_mode);
//            spinnerWifiIPMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
//                @Override
//                public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//                    findViewById(R.id.ll_wifi_static_ip_params).setVisibility(position == 0 ? View.VISIBLE : View.GONE);
//                }
//
//                @Override
//                public void onNothingSelected(AdapterView<?> parent) {
//
//                }
//            });
            edtWifiIP = findViewById(R.id.edt_wifi_ip);
            edtWifiIPMask = findViewById(R.id.edt_wifi_ip_mask);
            edtWifiIPGateway = findViewById(R.id.edt_wifi_ip_gateway);
            edtWifiIPDns = findViewById(R.id.edt_wifi_ip_dns);
            chkWifiAPItem = findViewById(R.id.chk_wifi_ap_item);
            edtWifiAPSsid = findViewById(R.id.edt_wifi_ap_ssid);
            edtWifiAPKey = findViewById(R.id.edt_wifi_ap_key);
            chkWifiSocketItem = findViewById(R.id.chk_wifi_socket_item);
            spinnerWifiSocketMode = findViewById(R.id.spinner_wifi_socket_mode);
            edtWifiSocketIP = findViewById(R.id.edt_wifi_socket_ip);
            edtWifiSocketPort = findViewById(R.id.edt_wifi_socket_port);
            chkWifiMacItem = findViewById(R.id.chk_wifi_mac_item);
            edtWifiMac = findViewById(R.id.edt_wifi_mac);
            findViewById(R.id.btn_clear_wifi_params).setOnClickListener(v -> clearWifiParamsAction());
            findViewById(R.id.btn_get_wifi_params).setOnClickListener(v -> getWifiParamsAction());
            findViewById(R.id.btn_set_wifi_params).setOnClickListener(v -> setWifiParamsAction());

            chkGprsSocketItem = findViewById(R.id.chk_gprs_socket_item);
            spinnerGprsSocketProtocol = findViewById(R.id.spinner_gprs_socket_protocol);
            edtGprsSocketIP = findViewById(R.id.edt_gprs_socket_ip);
            edtGprsSocketPort = findViewById(R.id.edt_gprs_socket_port);
            chkGprsAPNItem = findViewById(R.id.chk_gprs_apn_item);
            edtGprsApn = findViewById(R.id.edt_gprs_apn);
            spinnerGprsAuth = findViewById(R.id.spinner_gprs_auth);
            edtGprsApnAccount = findViewById(R.id.edt_gprs_apn_account);
            edtGprsApnPassword = findViewById(R.id.edt_gprs_apn_password);
            findViewById(R.id.btn_clear_gprs_params).setOnClickListener(v -> clearGprsParamsAction());
            findViewById(R.id.btn_get_gprs_params).setOnClickListener(v -> getGprsParamsAction());
            findViewById(R.id.btn_set_gprs_params).setOnClickListener(v -> setGprsParamsAction());

            spinnerWgProtocolFormat = findViewById(R.id.spinner_wg_protocol_format);
            edtWgDataStartAddress = findViewById(R.id.edt_wg_data_start_address);
            edtWgPulseWidth = findViewById(R.id.edt_wg_pulse_width);
            edtWgPulseCycle = findViewById(R.id.edt_wg_pulse_cycle);
            edtWgDataInterval = findViewById(R.id.edt_wg_data_interval);
            findViewById(R.id.btn_get_wg_params).setOnClickListener(v -> getWgParamsAction());
            findViewById(R.id.btn_set_wg_params).setOnClickListener(v -> setWgParamsAction());
        } // 通讯配置

        { // I/O控制
            chkIoInput1State = findViewById(R.id.chk_io_input1_state);
            findViewById(R.id.btn_get_io_input_state).setOnClickListener(v -> getIOInputStateAction());

            chkRelay1State = findViewById(R.id.chk_relay1_state);
            chkRelay2State = findViewById(R.id.chk_relay2_state);
            findViewById(R.id.btn_set_relay1_state).setOnClickListener(v -> setRelay1StateAction());
            findViewById(R.id.btn_set_relay2_state).setOnClickListener(v -> setRelay2StateAction());
        }

        { // 系统配置
            edtTransmitFrequencyStandard = findViewById(R.id.edt_transmit_frequency_standard);
            findViewById(R.id.btn_get_transmit_frequency_standard).setOnClickListener(v -> getTransmitFrequencyStandardAction());

            edtFirmwareVersion = findViewById(R.id.edt_firmware_version);
            findViewById(R.id.btn_get_firmware_version).setOnClickListener(v -> getFirmwareVersionAction());

            edtDeviceName = findViewById(R.id.edt_device_name);
            findViewById(R.id.btn_get_device_name).setOnClickListener(v -> getDeviceNameAction());
            findViewById(R.id.btn_set_device_name).setOnClickListener(v -> setDeviceNameAction());

            edtUpgradeFilePath = findViewById(R.id.edt_upgrade_file_path);
            findViewById(R.id.btn_select_upgrade_file).setOnClickListener(v -> selectUpgradeFileAction());
            findViewById(R.id.btn_upgrade).setOnClickListener(v -> upgradeAction());

            edtAllSystemParams = findViewById(R.id.edt_all_system_params);
            findViewById(R.id.btn_get_all_system_params).setOnClickListener(v -> getAllSystemParamsAction());

            findViewById(R.id.btn_get_reader_state).setOnClickListener(v -> getReaderStateAction());

            edtSN = findViewById(R.id.edt_sn);
            findViewById(R.id.btn_get_sn).setOnClickListener(v -> getSNAction());

            edtHeartbeatInterval = findViewById(R.id.edt_heartbeat_interval);
            findViewById(R.id.btn_set_heartbeat_interval).setOnClickListener(v -> setHeartbeatIntervalAction());
            findViewById(R.id.btn_send_heartbeat).setOnClickListener(v -> sendHeartbeatAction());

            findViewById(R.id.btn_reset_uhf).setOnClickListener(v -> resetUHFAction());
            findViewById(R.id.btn_reset_reader).setOnClickListener(v -> resetReaderAction());

            // 12个字符：[1..2]为`00`，[3]为5(S)或0(G)，[4..5]为产品序列，[6..7]为年份后两位，[8]为月份，[9..10]为日期，[11..12]取值：01 ～ FF。
            edtMacAddressGroup = new EditText[12];
            edtMacAddressGroup[0] = findViewById(R.id.edt_reader_mac_1st_char);
            edtMacAddressGroup[1] = findViewById(R.id.edt_reader_mac_2nd_char);
            // edtMacAddressGroup[2] = findViewById(R.id.edt_reader_mac_3rd_char);
            edtMacAddressGroup[3] = findViewById(R.id.edt_reader_mac_4th_char);
            edtMacAddressGroup[4] = findViewById(R.id.edt_reader_mac_5th_char);
            edtMacAddressGroup[5] = findViewById(R.id.edt_reader_mac_6th_char);
            edtMacAddressGroup[6] = findViewById(R.id.edt_reader_mac_7th_char);
            edtMacAddressGroup[7] = findViewById(R.id.edt_reader_mac_8th_char);
            edtMacAddressGroup[8] = findViewById(R.id.edt_reader_mac_9th_char);
            edtMacAddressGroup[9] = findViewById(R.id.edt_reader_mac_10th_char);
            edtMacAddressGroup[10] = findViewById(R.id.edt_reader_mac_11th_char);
            edtMacAddressGroup[11] = findViewById(R.id.edt_reader_mac_12th_char);
            Calendar calendar = Calendar.getInstance();
            final int year = calendar.get(Calendar.YEAR) % 100;//获取年份
            final int month = calendar.get(Calendar.MONTH) + 1;//获取月份
            final int day = calendar.get(Calendar.DATE);//获取日
            edtMacAddressGroup[5].setText(String.valueOf(year / 10));
            edtMacAddressGroup[6].setText(String.valueOf(year % 10));
            edtMacAddressGroup[7].setText(String.format("%X", month));
            edtMacAddressGroup[8].setText(String.valueOf(day / 10));
            edtMacAddressGroup[9].setText(String.valueOf(day % 10));

            spinnerProductType = findViewById(R.id.spinner_product_type);
            findViewById(R.id.btn_set_reader_mac).setOnClickListener(v -> setReaderMacAction());
        } // 系统配置

        { // 上报
            edtReport = findViewById(R.id.edt_report);
            edtReport.setKeyListener(null);
            edtReport.setMovementMethod(ScrollingMovementMethod.getInstance());
            findViewById(R.id.btn_clear_report).setOnClickListener(v -> edtReport.setText(""));
        } // 上报

        setControlStatusOnReaderConnectStatus(false);
        setControlStatusOnListenStatus(false);
    }

    private void searchReaderAction() {
        startActivityForResult(new Intent(MainActivity.this, NetReaderFindActivity.class), REQUEST_CODE_FOR_SEARCH_READER);
    }

    public static final String REGEX_IP_ADDR = "(2[5][0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d{2}|\\d{1,2})";
    private void connectReaderAction() {
        if (uhfReaderInterface != null) {
            return;
        }

        String connectStr;
        int communicationMode = spinnerReaderConnectMode.getSelectedItemPosition();
        if (communicationMode == -1) {
            Toast.makeText(this, "Please specify a communication mode.", Toast.LENGTH_SHORT).show();
            return;
        } else if (communicationMode == 0) { // Uart
            if (spinnerUart.getAdapter().isEmpty()) {
                Toast.makeText(this, "Please specify a uart.", Toast.LENGTH_SHORT).show();
                return;
            }
            connectStr = String.format(Locale.getDefault(), "CommType=COM;ComPath=%s;Address=1;Baud=%d;Databits=8;Stopbits=1;Parity=0", spinnerUart.getSelectedItem().toString(), Integer.parseInt(spinnerConnectBaud.getSelectedItem().toString()));
        } else if (communicationMode == 1) { // Network
            String ip = edtReaderIP.getText().toString().trim();
            if (!Pattern.matches(REGEX_IP_ADDR, ip)) {
                Toast.makeText(this, "Please input a valid ip.", Toast.LENGTH_SHORT).show();
                return;
            }

            Integer port = Utils.tryParseInt(edtReaderPort.getText().toString().trim());
            if (null == port || 1 > port || 65535 < port) {
                Toast.makeText(this, "Please input a valid port.", Toast.LENGTH_SHORT).show();
                return;
            }

            connectStr = String.format(Locale.getDefault(), "CommType=NET;RemoteIp=%s;RemotePort=%d", ip, port);
        } else {
            return;
        }

        uhfReaderInterface = new EraLinkUHFReaderInterface();
        final int result = uhfReaderInterface.connect(connectStr, this);
        if (EralinkApiErrDefinition.ErrNoError != result) {
            uhfReaderInterface = null;
            Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
            return;
        }

        setControlStatusOnReaderConnectStatus(true);

        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void setControlStatusOnReaderConnectStatus(boolean connected) {
        mainTabWidget.setCurrentTab(0);
        spinnerReaderWokeRole.setEnabled(!connected);
        spinnerReaderConnectMode.setEnabled(!connected);
        spinnerUart.setEnabled(!connected);
        spinnerConnectBaud.setEnabled(!connected);
        edtReaderIP.setEnabled(!connected);
        edtReaderPort.setEnabled(!connected);
        btnSearchReader.setEnabled(!connected);
        btnConnectReader.setEnabled(!connected);
        btnDisconnectReader.setEnabled(connected);
        for (int index = 0; index < mainTabWidget.getChildCount(); ++index) {
            mainTabWidget.getChildTabViewAt(index).setEnabled(connected);
        }

        btnStopInventory.setEnabled(false);
        btnStopAutoRead.setEnabled(false);
        btnStopMultiBankAutoRead.setEnabled(false);
    }

    private void disconnectReaderAction() {
        uhfReaderInterface.close();
        uhfReaderInterface = null;
        setControlStatusOnReaderConnectStatus(false);
    }

    private void startListenAction() {
        if (uhfReaderInterface != null) {
            return;
        }

        if (!validateStartListen()) {
            return;
        }

        uhfReaderInterface = new EraLinkUHFReaderInterface();
        final int result = uhfReaderInterface.listen("0.0.0.0", (short) 20058, this);
        if (EraLinkUHFReaderApiError.ErrNoError != result) {
            uhfReaderInterface = null;
        } else {
            setControlStatusOnListenStatus(true);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateStartListen() {
        if (!Utils.ipCheck(edtListenIP.getText().toString()) || !Utils.isValidPort(edtListenPort.getText().toString())) {
            Toast.makeText(this, "监听IP或监听端口非法。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void setControlStatusOnListenStatus(boolean listening) {
        mainTabWidget.setCurrentTab(0);
        spinnerReaderWokeRole.setEnabled(!listening);
        edtListenIP.setEnabled(!listening);
        edtListenPort.setEnabled(!listening);
        btnStartListen.setEnabled(!listening);
        btnStopListen.setEnabled(listening);
        for (int index = 0; index < mainTabWidget.getChildCount(); ++index) {
            mainTabWidget.getChildTabViewAt(index).setEnabled(listening);
        }

        btnStopInventory.setEnabled(false);
        btnStopAutoRead.setEnabled(false);
        btnStopMultiBankAutoRead.setEnabled(false);

        btnRemoveReader.setEnabled(false);
    }

    private void stopListenAction() {
        if (uhfReaderInterface != null) {
            uhfReaderInterface.close();
            uhfReaderInterface = null;

            reader_id = 0;
            readerMap.clear();
            readerList.clear();
            readerListViewAdapter.setSelectedItemId(-1);
            readerListViewAdapter.notifyDataSetChanged();
        }

        setControlStatusOnListenStatus(false);
    }

    private void removeReaderAction() {
        int index = 0;
        for (; index < readerList.size(); ++index) {
            if (reader_id == readerList.get(index).getReaderId()) {
                readerMap.remove(readerList.get(index).getSn());
                readerList.remove(index);
                uhfReaderInterface.removeReader(reader_id);
                break;
            }
        }
        if (readerList.isEmpty()) {
            reader_id = 0;
            readerListViewAdapter.setSelectedItemId(-1);
            btnRemoveReader.setEnabled(false);
        } else {
            if (0 < index) {
                index--;
                reader_id = readerList.get(index).getReaderId();
                readerListViewAdapter.setSelectedItemId(index);
            }
        }
        readerListViewAdapter.notifyDataSetChanged();
    }

    @Override
    public void report(byte reader_id, int report_cmd_code, final byte[] report_data, int report_data_len) {
        String message = String.format("[%s] reader_id: %s, report_cmd_code: %s, report_data: %s, report_data_len: %s.\n",
                Utils.getCurrentTime(), reader_id, report_cmd_code, StringUtils.bytesToHexString(report_data, 0, report_data_len), report_data_len);
        Log.d(TAG, message);

        if (report_cmd_code == EraLinkUHFReaderCmdCode.CmdInventoryOnce || report_cmd_code == EraLinkUHFReaderCmdCode.CmdStartInventory) {
            reportForInventory(reader_id, report_data, report_data_len);
        } else if (report_cmd_code == EraLinkUHFReaderCmdCode.CmdStartMultiBankAutoRead) {
            reportForMultiBankAutoRead(reader_id, report_data, report_data_len);
        } else if (report_cmd_code == EraLinkUHFReaderCmdCode.CmdStartAutoRead) {
            reportForAutoRead(reader_id, report_data, report_data_len);
        } else if (report_cmd_code == EraLinkUHFReaderCmdCode.CmdDeviceInfoReport) {
            reportReaderConnect(reader_id, report_data, report_data_len);
        } else if (report_cmd_code == EraLinkUHFReaderCmdCode.CmdReaderCommunicationBreak) {
            reportReaderDisconnect(reader_id);
        } else {
            reportForOthers(reader_id, message);
        }
    }

    private void reportForInventory(byte reader_id, final byte[] report_data, int report_data_len) {
        if (9 > report_data_len) {
            return;
        }

        tagReportTotalNumber.incrementAndGet();
        statisticsIntervalTagNumber.incrementAndGet();

        CacheNode cacheNode = CachePool.getInstance().obtain(1 + report_data_len);
        cacheNode.getBuffer()[0] = reader_id;
        System.arraycopy(report_data, 0, cacheNode.getBuffer(), 1, report_data_len);
        cacheNode.setBufferSize(1 + report_data_len);

        Message message = new Message();
        message.what = cmd_update_epc;
        message.obj = cacheNode;
        epcReportHandler.sendMessage(message);
    }

    private void reportForAutoRead(byte reader_id, final byte[] report_data, int report_data_len) {
        if (1 >= report_data_len) {
            return;
        }

        autoReadTagReportTotalNumber.incrementAndGet();
        autoReadStatisticsIntervalTagNumber.incrementAndGet();

        CacheNode cacheNode = CachePool.getInstance().obtain(1 + report_data_len);
        cacheNode.getBuffer()[0] = reader_id;
        System.arraycopy(report_data, 0, cacheNode.getBuffer(), 1, report_data_len);
        cacheNode.setBufferSize(1 + report_data_len);

        Message message = new Message();
        message.what = cmd_auto_read_update;
        message.obj = cacheNode;
        epcReportHandler.sendMessage(message);
    }

    private void reportForMultiBankAutoRead(byte reader_id, final byte[] report_data, int report_data_len) {
        if (1 >= report_data_len) {
            return;
        }

        CacheNode cacheNode = CachePool.getInstance().obtain(1 + report_data_len);
        cacheNode.getBuffer()[0] = reader_id;
        System.arraycopy(report_data, 0, cacheNode.getBuffer(), 1, report_data_len);
        cacheNode.setBufferSize(1 + report_data_len);

        Message message = new Message();
        message.what = cmd_multi_bank_auto_read_update;
        message.obj = cacheNode;
        epcReportHandler.sendMessage(message);
    }

    private void reportReaderConnect(byte reader_id, final byte[] report_data, int report_data_len) {
        if (reader_work_role == 0) {
            return;
        }
        final String device_info = new String(report_data, 0, report_data_len);
        final String[] fields = device_info.split(",");
        ReaderBean readerBean = new ReaderBean();
        readerBean.setReaderId(reader_id);
        if (fields.length == 2) {
            readerBean.setSn(fields[0]);
            readerBean.setName(fields[1]);
        } else { // fields.length == 3
            readerBean.setIp(fields[0]);
            readerBean.setSn(fields[1]);
            readerBean.setName(fields[2]);
        }

        Message message = new Message();
        message.what = cmd_reader_connect;
        message.obj = readerBean;
        epcReportHandler.sendMessage(message);
    }

    private void reportReaderDisconnect(byte reader_id) {
        Message message = new Message();
        message.what = cmd_reader_disconnect;
        message.obj = reader_id;
        epcReportHandler.sendMessage(message);
    }

    private void reportForOthers(byte reader_id, String report) {
        Message message = new Message();
        message.what = cmd_others_report;
        message.obj = report;
        epcReportHandler.sendMessage(message);
    }

    private void inventoryOnceAction() {
        final int result = uhfReaderInterface.inventoryOnce(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private byte getReaderId() {
        return reader_id;
    }

    private void inventoryAction() {
        if (inventorying) {
            return;
        }

        clearInventoryAction();

        final int result = uhfReaderInterface.inventory(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
        if (EralinkApiErrDefinition.ErrNoError != result) {
            return;
        }

        inventorying = true;
        lastMarkTime = 0;

        inventoryStatisticsTimer = new Timer();
        inventoryStatisticsTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                inventoryElapsedTime.addAndGet(100);
                epcReportHandler.obtainMessage(cmd_update_inventory_statistic).sendToTarget();
            }
        }, 0, 100);

        setControlStatusOnInventory();
    }

    private void setControlStatusOnInventory() {
        for (int index = 2; index < mainTabWidget.getChildCount(); ++index) {
            mainTabWidget.getChildTabViewAt(index).setEnabled(!inventorying);
        }
        for (int index = 1; index < tagTabWidget.getChildCount(); ++index) {
            tagTabWidget.getChildTabViewAt(index).setEnabled(!inventorying);
        }
        readTagTabWidget.getChildTabViewAt(1).setEnabled(!inventorying);

        btnInventoryOnce.setEnabled(!inventorying);
        btnInventory.setEnabled(!inventorying);
        btnStopInventory.setEnabled(inventorying);
        btnGetInventoryStatistics.setEnabled(!inventorying);
    }

    private void stopInventoryAction() {
        if (!inventorying) {
            return;
        }

        inventorying = false;

        inventoryStatisticsTimer.cancel();

        final int result = uhfReaderInterface.stopInventory(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();

        setControlStatusOnInventory();
    }

    private void clearInventoryAction() {
        tvInventoryElapsedTime.setText("00:00:00.0");
        inventoryElapsedTime.set(0);
        tvInventoryRate.setText("0");
        countForInventoryRate.set(0);
        statisticsIntervalTagNumber.set(0);
        tvInventoryTagNumber.setText("0");
        tvInventoryTagReportTotalNumber.setText("0");
        tagReportTotalNumber.set(0);
        epcBeanList.clear();
        epcBeanMap.clear();
        epcListViewAdapter.notifyDataSetChanged();
    }

    private void getInventoryStatisticsAction() {
        final byte[] statistics = new byte[64];
        final int[] statistics_len = new int[]{ statistics.length };
        final int result = uhfReaderInterface.getInventoryStatistics(getReaderId(), statistics, statistics_len);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            tvInventoryStatisticsDuration.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 0)));
            tvInventoryStatisticsQueryCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 4)));
            tvInventoryStatisticsReceivedRN16Count.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 8)));
            tvInventoryStatisticsRN16TimeoutCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 12)));
            tvInventoryStatisticsEPCTimeoutCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 16)));
            tvInventoryStatisticsTagReadCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 20)));
            tvInventoryStatisticsEPCCrcCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 24)));
            tvInventoryStatisticsInterfererBlockingDuration.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 28)));
            getEdtInventoryStatisticsInterfererBlockingCount.setText(String.valueOf(Utils.u32FromLittleEndianU8s(statistics, 32)));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void startAutoReadAction() {
        if (autoReading) {
            return;
        }

        clearAutoReadAction();

        final int result = uhfReaderInterface.startAutoRead(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
        if (EralinkApiErrDefinition.ErrNoError != result) {
            return;
        }

        autoReadTriggeredByManual = true;
        autoReading = true;
        autoReadLastMarkTime = 0;

        autoReadStatisticsTimer = new Timer();
        autoReadStatisticsTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                autoReadElapsedTime.addAndGet(100);
                epcReportHandler.obtainMessage(cmd_update_auto_read_statistic).sendToTarget();
            }
        }, 0, 100);

        setControlStatusOnAutoRead();
    }

    private void setControlStatusOnAutoRead() {
        for (int index = 2; index < mainTabWidget.getChildCount(); ++index) {
            mainTabWidget.getChildTabViewAt(index).setEnabled(!autoReading);
        }
        for (int index = 1; index < tagTabWidget.getChildCount(); ++index) {
            tagTabWidget.getChildTabViewAt(index).setEnabled(!autoReading);
        }
        readTagTabWidget.getChildTabViewAt(0).setEnabled(!autoReading);

        btnStartAutoRead.setEnabled(!autoReading);
        btnStopAutoRead.setEnabled(autoReading);
    }

    private void stopAutoReadAction() {
        if (!autoReading) {
            return;
        }

        autoReading = false;

        autoReadStatisticsTimer.cancel();

        final int result = uhfReaderInterface.stopAutoRead(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();

        setControlStatusOnAutoRead();
    }

    private void clearAutoReadAction() {
        tvAutoReadElapsedTime.setText("00:00:00.0");
        autoReadElapsedTime.set(0);
        tvAutoReadRate.setText("0");
        countForAutoReadRate.set(0);
        autoReadStatisticsIntervalTagNumber.set(0);
        tvAutoReadTagNumber.setText("0");
        tvAutoReadTagReportTotalNumber.setText("0");
        autoReadTagReportTotalNumber.set(0);
        autoReadEpcBeanList.clear();
        autoReadEpcBeanMap.clear();
        autoReadEpcListViewAdapter.notifyDataSetChanged();
    }

    private static class EPCReportHandler extends Handler {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        public EPCReportHandler(MainActivity mainActivity) {
            mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        public void handleMessage(Message msg) {
            switch (msg.what) {
                case cmd_report_io_failed:
                    processIOFailed((Integer) msg.obj);
                    break;
                case cmd_update_epc:
                    updateInventory(msg);
                    break;
                case cmd_update_inventory_statistic:
                    updateStatistics();
                    break;
                case cmd_multi_bank_auto_read_update:
                    updateMultiBankAutoRead(msg);
                    break;
                case cmd_auto_read_update:
                    updateAutoRead(msg);
                    break;
                case cmd_update_auto_read_statistic:
                    updateAutoReadStatistics();
                    break;
                case cmd_reader_connect:
                    processReaderConnect(msg);
                    break;
                case cmd_reader_disconnect:
                    processReaderDisconnect(msg);
                    break;
                case cmd_others_report:
                    othersReport(msg);
                default:
                    break;
            }
        }

        private void processIOFailed(int err_no) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            Toast.makeText(mainActivity, ErrString.getErrId(err_no), Toast.LENGTH_LONG).show();
        }

        private void updateInventory(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            CacheNode cacheNode = (CacheNode) msg.obj;
            final EPCBean epcBean = new EPCBean();
            EPCReportDataParseUtil.parseInventoryEPCReport(cacheNode, epcBean);
            CachePool.getInstance().release(cacheNode);

            EPCBean oldEpcBean = mainActivity.epcBeanMap.get(epcBean.getKey());
            if (oldEpcBean != null) {
                oldEpcBean.setRssi(epcBean.getRssi());
                oldEpcBean.setAnt(epcBean.getAnt());
                oldEpcBean.setCount(oldEpcBean.getCount() + 1);
            } else {
                epcBean.setIndex(mainActivity.epcBeanList.size());
                epcBean.setCount(1);
                mainActivity.epcBeanList.add(epcBean);
                mainActivity.epcBeanMap.put(epcBean.getKey(), epcBean);
            }

            mainActivity.epcListViewAdapter.notifyDataSetChanged();

            if (mainActivity.lastMarkTime == 0) {
                mainActivity.lastMarkTime = System.currentTimeMillis();
            }
            final long currentTime = System.currentTimeMillis();
            if (currentTime - mainActivity.lastMarkTime >= 90) {
                Beep.playOK();
                mainActivity.lastMarkTime = currentTime;
            }

            mainActivity.tvInventoryTagNumber.setText(String.valueOf(mainActivity.epcBeanMap.size()));
            mainActivity.tvInventoryTagReportTotalNumber.setText(String.valueOf(mainActivity.tagReportTotalNumber.get()));
        }

        private void updateStatistics() {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            final int milliseconds = (int)(mainActivity.inventoryElapsedTime.get() % 1000);
            final int totalSeconds = (int)((mainActivity.inventoryElapsedTime.get() - milliseconds) / 1000);
            final int seconds = totalSeconds % 60;
            final int minutes = (totalSeconds / 60) % 60;
            final int hours = totalSeconds / 3600;

            String strElapsedTime = String.format(Locale.CHINA, "%02d:%02d:%02d.%d",
                    hours, minutes, seconds, (int)((milliseconds / 1000.0) * 10));
            mainActivity.tvInventoryElapsedTime.setText(strElapsedTime);

            if (mainActivity.countForInventoryRate.incrementAndGet() >= 10) {
                mainActivity.tvInventoryRate.setText(String.valueOf(mainActivity.statisticsIntervalTagNumber));
                mainActivity.countForInventoryRate.set(0);
                mainActivity.statisticsIntervalTagNumber.set(0);
            }
        }

        private void updateMultiBankAutoRead(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            CacheNode cacheNode = (CacheNode) msg.obj;
            final EPCBean epcBean = new EPCBean();
            EPCReportDataParseUtil.parseMultiBankAutoReport(cacheNode, epcBean);
            CachePool.getInstance().release(cacheNode);

            EPCBean oldEPCBean = mainActivity.multiBankAutoReadEpcBeanMap.get(epcBean.getKey());
            if (oldEPCBean != null) {
                if (epcBean.getReserve() != null) {
                    oldEPCBean.setReserve(epcBean.getReserve());
                }
                if (epcBean.getTid() != null) {
                    oldEPCBean.setTid(epcBean.getTid());
                }
                if (epcBean.getUser() != null) {
                    oldEPCBean.setUser(epcBean.getUser());
                }
                oldEPCBean.setAnt(epcBean.getAnt());
                oldEPCBean.setCount(oldEPCBean.getCount() + 1);
            } else {
                epcBean.setIndex(mainActivity.multiBankAutoReadEpcBeanList.size());
                epcBean.setCount(1);
                mainActivity.multiBankAutoReadEpcBeanList.add(epcBean);
                mainActivity.multiBankAutoReadEpcBeanMap.put(epcBean.getKey(), epcBean);
            }

            mainActivity.multiBankAutoEpcListViewAdapter.notifyDataSetChanged();
        }
        
        private void updateAutoRead(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            CacheNode cacheNode = (CacheNode) msg.obj;
            final EPCBean epcBean = new EPCBean();
            EPCReportDataParseUtil.parseAutoReadReport(cacheNode, epcBean);
            CachePool.getInstance().release(cacheNode);

            if (!mainActivity.autoReadTriggeredByManual) {
                if (!mainActivity.autoReading) {
                    mainActivity.autoReading = true;
                    mainActivity.autoReadLastMarkTime = 0;

                    mainActivity.autoReadStatisticsTimer = new Timer();
                    mainActivity.autoReadStatisticsTimer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            mainActivity.autoReadElapsedTime.addAndGet(100);
                            mainActivity.epcReportHandler.obtainMessage(cmd_update_auto_read_statistic).sendToTarget();
                        }
                    }, 0, 100);

                    mainActivity.btnStartAutoRead.setEnabled(false);
                    mainActivity.btnStopAutoRead.setEnabled(true);
                }
            }

            mainActivity.tvAutoReadPcHead.setVisibility(epcBean.getPC() != null ? View.VISIBLE : View.GONE);
            mainActivity.tvAutoReadEpcHead.setVisibility(epcBean.getEPC() != null ? View.VISIBLE : View.GONE);
            mainActivity.tvAutoReadRssiHead.setVisibility(epcBean.getRssi() != null ? View.VISIBLE : View.GONE);
            mainActivity.tvAutoReadTidHead.setVisibility(epcBean.getTid() != null ? View.VISIBLE : View.GONE);
            mainActivity.tvAutoReadUserHead.setVisibility(epcBean.getUser() != null ? View.VISIBLE : View.GONE);
            mainActivity.autoReadEpcListViewAdapter.setHidePCColumn(epcBean.getPC() == null);
            mainActivity.autoReadEpcListViewAdapter.setHideEpcColumn(epcBean.getEPC() == null);
            mainActivity.autoReadEpcListViewAdapter.setHideRssiColumn(epcBean.getRssi() == null);
            mainActivity.autoReadEpcListViewAdapter.setHideTidColumn(epcBean.getTid() == null);
            mainActivity.autoReadEpcListViewAdapter.setHideUserColumn(epcBean.getUser() == null);

            String key = epcBean.getEPC();
            if (key == null) {
                key = epcBean.getTid();
                if (key == null) {
                    key = epcBean.getUser();
                }
            }

            EPCBean oldEpcBean = mainActivity.autoReadEpcBeanMap.get(key);
            if (oldEpcBean != null) {
                oldEpcBean.setRssi(epcBean.getRssi());
                oldEpcBean.setAnt(epcBean.getAnt());
                oldEpcBean.setCount(oldEpcBean.getCount() + 1);
                if (epcBean.getTid() != null) {
                    oldEpcBean.setTid(epcBean.getTid());
                }
                if (epcBean.getUser() != null) {
                    oldEpcBean.setUser(epcBean.getUser());
                }
            } else {
                epcBean.setIndex(mainActivity.autoReadEpcBeanList.size());
                epcBean.setCount(1);
                mainActivity.autoReadEpcBeanList.add(epcBean);
                mainActivity.autoReadEpcBeanMap.put(key, epcBean);
            }

            mainActivity.autoReadEpcListViewAdapter.notifyDataSetChanged();

            if (mainActivity.autoReadLastMarkTime == 0) {
                mainActivity.autoReadLastMarkTime = System.currentTimeMillis();
            }
            final long currentTime = System.currentTimeMillis();
            if (currentTime - mainActivity.autoReadLastMarkTime >= 90) {
                Beep.playOK();
                mainActivity.autoReadLastMarkTime = currentTime;
            }

            mainActivity.tvAutoReadTagNumber.setText(String.valueOf(mainActivity.autoReadEpcBeanMap.size()));
            mainActivity.tvAutoReadTagReportTotalNumber.setText(String.valueOf(mainActivity.autoReadTagReportTotalNumber.get()));
        }

        private void updateAutoReadStatistics() {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            final int milliseconds = (int)(mainActivity.autoReadElapsedTime.get() % 1000);
            final int totalSeconds = (int)((mainActivity.autoReadElapsedTime.get() - milliseconds) / 1000);
            final int seconds = totalSeconds % 60;
            final int minutes = (totalSeconds / 60) % 60;
            final int hours = totalSeconds / 3600;

            String strElapsedTime = String.format(Locale.CHINA, "%02d:%02d:%02d.%d",
                    hours, minutes, seconds, (int)((milliseconds / 1000.0) * 10));
            mainActivity.tvAutoReadElapsedTime.setText(strElapsedTime);

            if (mainActivity.countForAutoReadRate.incrementAndGet() >= 10) {
                mainActivity.tvAutoReadRate.setText(String.valueOf(mainActivity.autoReadStatisticsIntervalTagNumber));
                mainActivity.countForAutoReadRate.set(0);
                mainActivity.autoReadStatisticsIntervalTagNumber.set(0);
            }
        }

        private void processReaderConnect(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            ReaderBean readerBean = (ReaderBean) msg.obj;

            if (mainActivity.readerMap.containsKey(readerBean.getSn())) {
                for (ReaderBean reader : mainActivity.readerList) {
                    if (reader.getSn().equals(readerBean.getSn())) {
                        mainActivity.readerList.remove(reader);
                        break;
                    }
                }
            }
            mainActivity.readerList.add(readerBean);
            mainActivity.readerMap.put(readerBean.getSn(), readerBean);
            if (mainActivity.readerListViewAdapter.getSelectedItemId() == -1) {
                mainActivity.readerListViewAdapter.setSelectedItemId(0);
                mainActivity.reader_id = mainActivity.readerList.get(0).getReaderId();
            }
            mainActivity.readerListViewAdapter.notifyDataSetChanged();
            mainActivity.btnRemoveReader.setEnabled(true);
        }

        private void processReaderDisconnect(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            Byte reader_id = (Byte) msg.obj;
            int index = 0;
            for (; index < mainActivity.readerList.size(); ++index) {
                if (reader_id == mainActivity.readerList.get(index).getReaderId()) {
                    mainActivity.readerMap.remove(mainActivity.readerList.get(index).getSn());
                    mainActivity.readerList.remove(index);
                    break;
                }
            }
            if (mainActivity.readerList.isEmpty()) {
                mainActivity.reader_id = 0;
                mainActivity.readerListViewAdapter.setSelectedItemId(-1);
                mainActivity.btnRemoveReader.setEnabled(false);
            } else {
                if (0 < index) {
                    index--;
                    mainActivity.reader_id = mainActivity.readerList.get(index).getReaderId();
                    mainActivity.readerListViewAdapter.setSelectedItemId(index);
                }
            }
            mainActivity.readerListViewAdapter.notifyDataSetChanged();
        }

        private void othersReport(Message msg) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            final String message = (String) msg.obj;
            mainActivity.edtReport.append(message);
            mainActivity.edtReport.setSelection(mainActivity.edtReport.getText().length(), mainActivity.edtReport.getText().length());
        }
    }

    private final Handler epcReportHandler = new EPCReportHandler(this);

    private void readTagAction() {
        if (!validateReadTagParams()) {
            return;
        }

        final byte bank = (byte) spinnerTagAccessBank.getSelectedItemId();
        final short offset = (short) Integer.parseInt(edtTagAccessOffset.getText().toString());
        final byte length = (byte) Integer.parseInt(edtTagAccessLength.getText().toString());
        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final byte[] tagData = new byte[2 * length];
        final int result = uhfReaderInterface.readTag(getReaderId(), bank, offset, length, accessPassword, tagData);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtTagData.setText(StringUtils.bytesToHexString(tagData, 0, tagData.length));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateReadTagParams() {
        if (!validateAccessPassword()) {
            Toast.makeText(this, "访问密码为8个字节长的十六进制串。", Toast.LENGTH_SHORT).show();
            return false;
        }

        if (!correctOffsetAndWordCount()) {
            return false;
        }

        return true;
    }

    private boolean validateAccessPassword() {
        final String accessPassword = edtTagAccessPassword.getText().toString().toUpperCase();
        if (accessPassword.length() != 8) {
            showUserMessageShort("访问密码为4字节十六进制串。");
            return false;
        }
        return StringUtils.isHexString(accessPassword);
    }

    private boolean correctOffsetAndWordCount() {
        if (edtTagAccessOffset.getText().toString().equals("")) {
            edtTagAccessOffset.setText("0");
            Toast.makeText(this, "偏移不能为空，取值：0 ～ 65535。", Toast.LENGTH_SHORT).show();
            return false;
        }

        final int offset = Integer.parseInt(edtTagAccessOffset.getText().toString());
        if (65535 < offset) {
            Toast.makeText(this, "偏移取值：0 ～ 65535。", Toast.LENGTH_SHORT).show();
        }

        final String wordCount = edtTagAccessLength.getText().toString();
        if (wordCount.equals("")) {
            edtTagAccessLength.setText("6");
            Toast.makeText(this, "长度不能为空，取值：1 ～ 255。", Toast.LENGTH_SHORT).show();
            return false;
        }

        final int length = Integer.parseInt(wordCount);
        if (0 == length || 255 < length) {
            Toast.makeText(this, "长度不能为空，取值：1 ～ 255。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void writeTagAction() {
        if (!validateWriteTagParams()) {
            return;
        }

        final byte bank = (byte) spinnerTagAccessBank.getSelectedItemId();
        final short offset = (short) Integer.parseInt(edtTagAccessOffset.getText().toString());
        final byte length = (byte) Integer.parseInt(edtTagAccessLength.getText().toString());
        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final byte[] tagData = StringUtils.hexStrToBytes(edtTagData.getText().toString());
        final int result = uhfReaderInterface.writeTag(getReaderId(), bank, offset, length, accessPassword, tagData);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateWriteTagParams() {
        if (!validateReadTagParams()) {
            return false;
        }

        final int length = Integer.parseInt(edtTagAccessLength.getText().toString());
        final String tagData = edtTagData.getText().toString();
        if (!StringUtils.isHexString(tagData) || tagData.length() / 2 != 2 * length) {
            Toast.makeText(this, "标签数据为长度为2倍长度字节长的十六进制串。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void tagBlockWriteAction() {
        if (!validateBlockWriteParams()) {
            return;
        }

        final byte bank = (byte) spinnerTagAccessBank.getSelectedItemId();
        final short offset = (short) Integer.parseInt(edtTagAccessOffset.getText().toString());
        final byte length = (byte) Integer.parseInt(edtTagAccessLength.getText().toString());
        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final byte[] tagData = StringUtils.hexStrToBytes(edtTagData.getText().toString());
        final int result = uhfReaderInterface.blockWrite(getReaderId(), bank, offset, length, accessPassword, tagData);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateBlockWriteParams() {
        if (!validateWriteTagParams()) {
            return false;
        }

        final int length = Integer.parseInt(edtTagAccessLength.getText().toString());
        if (length != 8) {
            Toast.makeText(this, "块写时长度必须为8。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void tagBlockEraseAction() {
        if (!validateReadTagParams()) {
            return;
        }

        final byte bank = (byte) spinnerTagAccessBank.getSelectedItemId();
        final short offset = (short) Integer.parseInt(edtTagAccessOffset.getText().toString());
        final byte length = (byte) Integer.parseInt(edtTagAccessLength.getText().toString());
        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final int result = uhfReaderInterface.blockErase(getReaderId(), bank, offset, length, accessPassword);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void lockTagAction() {
        if (!validateAccessPassword()) {
            return;
        }

        final byte lock_type = (byte) spinnerTagLockType.getSelectedItemId();
        final byte lock_bank = (byte) spinnerTagLockBank.getSelectedItemId();
        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final int result = uhfReaderInterface.lockTag(getReaderId(), lock_type, lock_bank, accessPassword);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private boolean validateKillPassword() {
        final String killPassword = edtTagKillPassword.getText().toString().toUpperCase();
        if (killPassword.length() != 8) {
            showUserMessageShort("销毁密码为4字节十六进制串。");
            return false;
        }
        return StringUtils.isHexString(killPassword);
    }

    private void killTagAction() {
        if (!validateAccessPassword() || !validateKillPassword()) {
            return;
        }

        final byte[] accessPassword = StringUtils.hexStrToBytes(edtTagAccessPassword.getText().toString());
        final byte[] killPassword = StringUtils.hexStrToBytes(edtTagKillPassword.getText().toString());
        final int result = uhfReaderInterface.killTag(getReaderId(), killPassword, accessPassword);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void getTagSelectAction() {
        final byte[] select_tag = new byte[128];
        final int[] select_tag_len = new int[]{ select_tag.length };
        final int result = uhfReaderInterface.getSelectTag(getReaderId(), select_tag, select_tag_len);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            if (1 < select_tag_len[0] && select_tag[0] == 0x01) {
                spinnerTagSelectBank.setSelection(select_tag[1] & 0xFF);
                edtTagSelectOffset.setText(String.valueOf((Utils.u16FromLittleEndianU8s(select_tag, 2) & 0xFFFF) >> 4));
                edtTagSelectLength.setText(String.valueOf((Utils.u16FromLittleEndianU8s(select_tag, 4) & 0xFFFF) >> 4));
                edtTagSelectData.setText(StringUtils.bytesToHexString(select_tag, 6, select_tag_len[0]- 5));
            }
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void tagSelectAction() {
        final short[] tag_select_offset = new short[1];
        final short[] tag_select_length = new short[1];
        final byte[][] tag_select_data = new byte[1][1];
        if (!validateTagSelect(tag_select_offset, tag_select_length, tag_select_data)) {
            return;
        }

        final byte bank = (byte) spinnerTagSelectBank.getSelectedItemId();
        final int result = uhfReaderInterface.selectTag(
                getReaderId(), bank, tag_select_offset[0], tag_select_length[0], tag_select_data[0]);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateTagSelect(final short[] tag_select_offset, final short[] tag_select_length,
                                      final byte[][] tag_select_data) {
        final Integer offset = Utils.tryParseInt(edtTagSelectOffset.getText().toString());
        final Integer length = Utils.tryParseInt(edtTagSelectLength.getText().toString());
        final String tag_data = edtTagSelectData.getText().toString();
        if (offset != null && offset <= 65535 && length != null && 0 < length && length <= 65535 &&
                tag_data.length() % 2 == 0 && tag_data.length() == 4 * length) {
            tag_select_offset[0] = offset.shortValue();
            tag_select_length[0] = length.shortValue();
            tag_select_data[0] = StringUtils.hexStrToBytes(tag_data);
            return true;
        }

        Toast.makeText(this, "标签选择偏移取值：0 ～ 65535。\n" +
                "标签选择长度取值：1 ～ 65535。\n" +
                "标签选择数据为长度为2倍长度字节长的十六进制串。", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void cancelTagSelectAction() {
        final int result = uhfReaderInterface.cancelSelectTag(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void clearMultiBankAutoReadAction() {
        multiBankAutoReadEpcBeanMap.clear();
        multiBankAutoReadEpcBeanList.clear();
        multiBankAutoEpcListViewAdapter.notifyDataSetChanged();
    }

    private void startMultiBankAutoReadAction() {
        final byte[] params = new byte[17];
        if (!buildStartMultiBankAutoReadParams(params)) {
            return;
        }

        final int result = uhfReaderInterface.startMultiBankAutoRead(getReaderId(), params);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
        if (EraLinkUHFReaderApiError.ErrNoError != result) {
            return;
        }

        multiBankAutoReading = true;
        setControlStatusOnMultiBankAutoRead();
    }

    private boolean buildStartMultiBankAutoReadParams(final byte[] params) {
        if (chkMultiBankAutoReadReserve.isChecked()) {
            Integer offset = Utils.tryParseInt(edtMultiBankAutoReadReserveOffset.getText().toString());
            Integer length = Utils.tryParseInt(edtMultiBankAutoReadReserveLength.getText().toString());
            if (offset == null || 65535 < offset || length == null || length == 0 || 255 < length) {
                Toast.makeText(this, "多区域自动读Reserve区偏移取值：0 ～ 65535，长度取值：1 ～ 255。" , Toast.LENGTH_SHORT).show();
                return false;
            }

            params[0] |= 0b01;
            Utils.u16ToLittleEndianU8s(offset.shortValue(), params, 1);
            params[3] = length.byteValue();
        }

        if (chkMultiBankAutoReadTid.isChecked()) {
            Integer offset = Utils.tryParseInt(edtMultiBankAutoReadTidOffset.getText().toString());
            Integer length = Utils.tryParseInt(edtMultiBankAutoReadTidLength.getText().toString());
            if (offset == null || 65535 < offset || length == null || length == 0 || 255 < length) {
                Toast.makeText(this, "多区域自动读Tid区偏移取值：0 ～ 65535，长度取值：1 ～ 255。" , Toast.LENGTH_SHORT).show();
                return false;
            }

            params[0] |= (0b01 << 2);
            Utils.u16ToLittleEndianU8s(offset.shortValue(), params, 7);
            params[9] = length.byteValue();
        }

        if (chkMultiBankAutoReadUser.isChecked()) {
            Integer offset = Utils.tryParseInt(edtMultiBankAutoReadUserOffset.getText().toString());
            Integer length = Utils.tryParseInt(edtMultiBankAutoReadUserLength.getText().toString());
            if (offset == null || 65535 < offset || length == null || length == 0 || 255 < length) {
                Toast.makeText(this, "多区域自动读User区偏移取值：0 ～ 65535，长度取值：1 ～ 255。" , Toast.LENGTH_SHORT).show();
                return false;
            }

            params[0] |= (0b01 << 3);
            Utils.u16ToLittleEndianU8s(offset.shortValue(), params, 10);
            params[12] = length.byteValue();
        }

        if (0 == (params[0] & 0b1101)) {
            Toast.makeText(this, "多区域自动读必须至少选择一个区域。" , Toast.LENGTH_SHORT).show();
            return false;
        }

        final String accessPassword = edtMultiBankAutoReadAccessPassword.getText().toString().toUpperCase();
        if (accessPassword.length() != 8 || !StringUtils.isHexString(accessPassword)) {
            Toast.makeText(this, "多区域自动读访问密码为4字节的十六进制串。" , Toast.LENGTH_SHORT).show();
            return false;
        }

        final byte[] access_password = StringUtils.hexStrToBytes(accessPassword);
        System.arraycopy(access_password, 0, params, 13, 4);

        return true;
    }

    private void setControlStatusOnMultiBankAutoRead() {
        for (int index = 2; index < mainTabWidget.getChildCount(); ++index) {
            mainTabWidget.getChildTabViewAt(index).setEnabled(!multiBankAutoReading);
        }
        for (int index = 0; index < tagTabWidget.getChildCount() - 1; ++index) {
            tagTabWidget.getChildTabViewAt(index).setEnabled(!multiBankAutoReading);
        }

        btnStartMultiBankAutoRead.setEnabled(!multiBankAutoReading);
        btnStopMultiBankAutoRead.setEnabled(multiBankAutoReading);
    }

    private void stopMultiBankAutoReadAction() {
        if (!multiBankAutoReading) {
            return;
        }

        multiBankAutoReading = false;

        final int result = uhfReaderInterface.stopMultiBankAutoRead(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();

        setControlStatusOnMultiBankAutoRead();
    }

    private void refreshAntConfigAction() {
        if (!chkAntEchoDetectingEnable.isChecked()) {
            showUserMessageShort("天线回波检测未启用。");
            return;
        }

        showUserDialogNoCancel("天线刷新中。。。");
        RefreshAntsTask task = new RefreshAntsTask(this);
        task.execute();
    }

    private void getAntConfigAction() {
        final int result = uhfReaderInterface.getAntConfig(getReaderId(), ant_config, ant_config_len);
        if (EralinkApiErrDefinition.ErrNoError == result) {
            ant_num = ant_config_len[0] / 12;
            for (int index = 1; index < ant_num; ++index) {
                llAntConfigs[index].setVisibility(View.VISIBLE);
            }
            for (int index = ant_num; index < llAntConfigs.length; ++index) {
                llAntConfigs[index].setVisibility(View.GONE);
            }

            testAntList.clear();
            for (int index = 0; index < ant_num; ++index) {
                final int enable = Utils.u32FromLittleEndianU8s(ant_config, index * 12);
                final int dwellTime = Utils.u32FromLittleEndianU8s(ant_config, index * 12 + 4);
                final int power = Utils.u32FromLittleEndianU8s(ant_config, index * 12 + 8) / 10;
                chkAntEnableGroup[index].setChecked(enable == 1);
                Integer selectIndex = dwellTimes.get(dwellTime);
                if (selectIndex != null) {
                    spinnerAntDwellTimeGroup[index].setSelection(selectIndex);
                }
                selectIndex = outputPowers.get(power);
                if (selectIndex != null) {
                    spinnerAntOutputPowerGroup[index].setSelection(selectIndex);
                }

                testAntList.add(index + 1);
            }
            testAntListAdapter.notifyDataSetChanged();
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setAntConfigAction() {
        final byte[] antConfig = new byte[12 * ant_num];
        for (int index = 0; index < ant_num; ++index) {
            final int enable = chkAntEnableGroup[index].isChecked() ? 1 : 0;
            final int power = Integer.parseInt(spinnerAntOutputPowerGroup[index].getSelectedItem().toString()) * 10;
            final int dwellTime =  Integer.parseInt(spinnerAntDwellTimeGroup[index].getSelectedItem().toString());
            Utils.u32ToLittleEndianU8s(enable, antConfig, index * 12);
            Utils.u32ToLittleEndianU8s(dwellTime, antConfig, index * 12 + 4);
            Utils.u32ToLittleEndianU8s(power, antConfig, index * 12 + 8);
        }

        final int result = uhfReaderInterface.setAntConfig(getReaderId(), antConfig);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void clearAntConfigAction() {
        for (int index = 0; index < ant_num; ++index) {
            chkAntEnableGroup[index].setChecked(false);
            spinnerAntDwellTimeGroup[index].setSelection(0, true);
            spinnerAntOutputPowerGroup[index].setSelection(0, true);
        }
    }

    private void antEchoDetectingEnableAction() {
        final int address = 0x0000009F;
        final int value = chkAntEchoDetectingEnable.isChecked() ? 0x01100000 : 0x01100010;
        final int result = uhfReaderInterface.writeOemRegister(getReaderId(), address, value);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
        if (!chkAntEchoDetectingEnable.isChecked()) {
            for (int index = 0; index < ant_num; ++index) {
                chkAntEnableGroup[index].setEnabled(true);
            }
        }
    }

    private void readMacRegisterAction() {
        String macRegisterAddress = edtMacRegisterAddress.getText().toString();
        if (macRegisterAddress.isEmpty()) {
            Toast.makeText(this, R.string.register_address_hint, Toast.LENGTH_SHORT).show();
            return;
        }

        final int registerAddress = new BigInteger(macRegisterAddress, 16).intValue();

        final int[] registerValue = new int[1];
        final int result = uhfReaderInterface.readMacRegister(getReaderId(), registerAddress, registerValue);
        if (EralinkApiErrDefinition.ErrNoError == result) {
            edtMacRegisterValue.setText(Integer.toHexString(registerValue[0]).toUpperCase());
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void writeMacRegisterAction() {
        String macRegisterAddress = edtMacRegisterAddress.getText().toString();
        if (macRegisterAddress.isEmpty()) {
            Toast.makeText(this, R.string.register_address_hint, Toast.LENGTH_SHORT).show();
            return;
        }
        String macRegisterValue = edtMacRegisterValue.getText().toString();
        if (macRegisterValue.isEmpty()) {
            Toast.makeText(this, R.string.register_value_hint, Toast.LENGTH_SHORT).show();
            return;
        }

        final int registerAddress = new BigInteger(macRegisterAddress, 16).intValue();
        final int registerValue = new BigInteger(macRegisterValue, 16).intValue();

        final int result = uhfReaderInterface.writeMacRegister(getReaderId(), registerAddress, registerValue);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void readOemRegisterAction() {
        String oemRegisterAddress = edtOemRegisterAddress.getText().toString();
        if (oemRegisterAddress.isEmpty()) {
            Toast.makeText(this, R.string.register_address_hint, Toast.LENGTH_SHORT).show();
            return;
        }

        final int registerAddress = new BigInteger(oemRegisterAddress, 16).intValue();

        final int[] registerValue = new int[1];
        final int result = uhfReaderInterface.readOemRegister(getReaderId(), registerAddress, registerValue);
        if (EralinkApiErrDefinition.ErrNoError == result) {
            edtOemRegisterValue.setText(Integer.toHexString(registerValue[0]).toUpperCase());
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void writeOemRegisterAction() {
        String oemRegisterAddress = edtOemRegisterAddress.getText().toString();
        if (oemRegisterAddress.isEmpty()) {
            Toast.makeText(this, R.string.register_address_hint, Toast.LENGTH_SHORT).show();
            return;
        }
        String oemRegisterValue = edtOemRegisterValue.getText().toString();
        if (oemRegisterValue.isEmpty()) {
            Toast.makeText(this, R.string.register_value_hint, Toast.LENGTH_SHORT).show();
            return;
        }

        final int registerAddress = new BigInteger(oemRegisterAddress, 16).intValue();
        final int registerValue = new BigInteger(oemRegisterValue, 16).intValue();

        final int result = uhfReaderInterface.writeOemRegister(getReaderId(), registerAddress, registerValue);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getLinkFrequencyAction() {
        final byte[] link_profile = new byte[1];
        final int result = uhfReaderInterface.getLinkProfile(getReaderId(), link_profile);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            if ((link_profile[0] & 0xFF) <= 4) {
                spinnerLinkFrequency.setSelection(link_profile[0]);
            }
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setLinkFrequencyAction() {
        final byte link_profile = (byte) spinnerLinkFrequency.getSelectedItemId();
        final int result = uhfReaderInterface.setLinkProfile(getReaderId(), link_profile);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void selectFrequencyBandFileAction() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        startActivityForResult(intent, REQUEST_CODE_FOR_FREQUENCY_FILE);
    }

    private void setFrequencyBandAction() {
        if (frequencyBandFileUri == null) {
            Toast.makeText(this, "请选择频段文件。", Toast.LENGTH_SHORT).show();
            return;
        }

        showUserDialogNoCancel("频段更新中。。。");
        UpdateFrequencyBandTask task = new UpdateFrequencyBandTask(this);
        task.execute();
    }

    private void getTxOnOffTimeAction() {
        final byte[] mode = new byte[1];
        final short[] tx_on_time = new short[1];
        final short[] tx_off_time = new short[1];
        final int result = uhfReaderInterface.getTxOnOffTime(getReaderId(), mode, tx_on_time, tx_off_time);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            chkTxOffTimeEnable.setChecked((mode[0] & 0xFF) == 0x11);
            edtTxOnTime.setText(String.valueOf(tx_on_time[0]));
            edtTxOffTime.setText(String.valueOf(tx_off_time[0]));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setTxOnOffTimeAction() {
        if (!validateSetTxOnOffTimeParams()) {
            Toast.makeText(this, "Tx On/Off Time为能为空。", Toast.LENGTH_SHORT).show();
            return;
        }

        final byte mode = (byte) (chkTxOffTimeEnable.isChecked() ? 0x11 : 0x01);
        final short tx_on_time = (short) Integer.parseInt(edtTxOnTime.getText().toString());
        final short tx_off_time = (short) Integer.parseInt(edtTxOffTime.getText().toString());
        final int result = uhfReaderInterface.setTxOnOffTime(getReaderId(), mode, tx_on_time, tx_off_time);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetTxOnOffTimeParams() {
        return !edtTxOnTime.getText().toString().isEmpty() && !edtTxOffTime.getText().toString().isEmpty();
    }

    private void getQValueAction() {
        final byte[] mode = new byte[1];
        final byte[] start_q_value = new byte[1];
        final byte[] max_q_value = new byte[1];
        final byte[] min_q_value = new byte[1];
        final byte[] threshold_multiplier = new byte[1];
        final int result = uhfReaderInterface.getQValue(getReaderId(), mode, start_q_value, max_q_value,
                min_q_value, threshold_multiplier);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerQValueMode.setSelection(mode[0]);
            edtStartQValue.setText(String.valueOf(start_q_value[0]));
            if (1 == mode[0]) {
                edtMaxQValue.setText(String.valueOf(max_q_value[0]));
                edtMinQValue.setText(String.valueOf(min_q_value[0]));
                edtThresholdMultiplier.setText(String.valueOf(threshold_multiplier[0]));
            }
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setQValueAction() {
        final byte[] mode = new byte[1];
        final byte[] start_q_value = new byte[1];
        final byte[] max_q_value = new byte[1];
        final byte[] min_q_value = new byte[1];
        final byte[] threshold_multiplier = new byte[1];

        if (!validateSetQValueParams(mode, start_q_value, max_q_value, min_q_value, threshold_multiplier)) {
            return;
        }
        final int result = uhfReaderInterface.setQValue(getReaderId(), mode[0], start_q_value[0], max_q_value[0],
                min_q_value[0], threshold_multiplier[0]);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetQValueParams(final byte[] mode, final byte[] start_q_value, final byte[] max_q_value,
        final byte[] min_q_value, final byte[] threshold_multiplier) {
        mode[0] = (byte) spinnerQValueMode.getSelectedItemId();

        final Integer startQValue = Utils.tryParseInt(edtStartQValue.getText().toString());
        final Integer maxQValue = Utils.tryParseInt(edtMaxQValue.getText().toString());
        final Integer minQValue = Utils.tryParseInt(edtMinQValue.getText().toString());
        final Integer thresholdMultiplier = Utils.tryParseInt(edtThresholdMultiplier.getText().toString());

        if (startQValue != null && 0 <= startQValue && startQValue <= 255) {
            if (mode[0] == 1) {
                if (maxQValue != null && 0 <= maxQValue && maxQValue <= 255 &&
                        minQValue != null && 0 <= minQValue && minQValue <= 255 &&
                        thresholdMultiplier != null && 0 <= thresholdMultiplier && thresholdMultiplier <= 255) {
                    start_q_value[0] = startQValue.byteValue();
                    max_q_value[0] = maxQValue.byteValue();
                    min_q_value[0] = minQValue.byteValue();
                    threshold_multiplier[0] = thresholdMultiplier.byteValue();
                    return true;
                }
            } else {
                start_q_value[0] = startQValue.byteValue();
                return true;
            }
        }

        Toast.makeText(this, "Q Value/Start Q Value：0 ~ 255.\n" +
                "Max Q Value：0 ~ 255.\n" +
                "Min Q Value：0 ~ 255.\n" +
                "Threshold Multiplier：0 ~ 255.", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void getQueryConfigAction() {
        final byte[] query_target = new byte[1];
        final byte[] query_session = new byte[1];
        final byte[] query_select = new byte[1];
        final int result = uhfReaderInterface.getQueryConfig(getReaderId(), query_target, query_session, query_select);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerQueryTarget.setSelection(query_target[0]);
            spinnerQuerySession.setSelection(query_session[0]);
            spinnerQuerySelect.setSelection(query_select[0]);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setQueryConfigAction() {
        byte query_target = (byte) spinnerQueryTarget.getSelectedItemId();
        byte query_session = (byte) spinnerQuerySession.getSelectedItemId();
        byte query_select = (byte) spinnerQuerySelect.getSelectedItemId();
        final int result = uhfReaderInterface.setQueryConfig(getReaderId(), query_target, query_session, query_select);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getInventoryConfigAction() {
        final byte[] inventory_algorithm = new byte[1];
        final byte[] match_report = new byte[1];
        final byte[] auto_select = new byte[1];
        final byte[] disable_inventory = new byte[1];
        final int result = uhfReaderInterface.getInventoryConfig(
                getReaderId(), inventory_algorithm, match_report, auto_select, disable_inventory);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtInventoryConfigAlgorithm.setText(String.valueOf(inventory_algorithm[0]));
            edtInventoryConfigMatchReport.setText(String.valueOf(match_report[0]));
            chkInventoryConfigAutoSelect.setChecked(auto_select[0] == 1);
            chkInventoryConfigDisableInventory.setChecked(disable_inventory[0] == 1);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setInventoryConfigAction() {
        final byte[] inventory_algorithm = new byte[1];
        final byte[] match_report = new byte[1];
        if (!validateSetInventoryConfig(inventory_algorithm, match_report)) {
            return;
        }

        final byte auto_select = (byte) (chkInventoryConfigAutoSelect.isChecked() ? 1 : 0);
        final byte disable_inventory = (byte) (chkInventoryConfigDisableInventory.isChecked() ? 1 : 0);
        final int result = uhfReaderInterface.setInventoryConfig(
                getReaderId(), inventory_algorithm[0], match_report[0], auto_select, disable_inventory);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getCarrierAdjustTimeAction() {
        final byte[] adjust_time = new byte[1];
        final int result = uhfReaderInterface.getAdjustCwConfig(getReaderId(), adjust_time);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtCarrierAdjustTime.setText(String.valueOf(adjust_time[0] & 0xFF));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setCarrierAdjustTimeAction() {
        final byte[] adjust_time = new byte[1];
        if (!validateSetCarrierAdjustTime(adjust_time)) {
            return;
        }

        final int result = uhfReaderInterface.setAdjustCwConfig(getReaderId(), adjust_time[0]);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();

        findViewById(R.id.btn_cw_on).setOnClickListener(v -> cwOnAction());
        findViewById(R.id.btn_cw_off).setOnClickListener(v -> cwOffAction());
        findViewById(R.id.btn_hopping_on).setOnClickListener(v -> hoppingOnAction());
        findViewById(R.id.btn_hopping_off).setOnClickListener(v -> hoppingOffAction());
        findViewById(R.id.btn_set_test_ant_power).setOnClickListener(v -> setTestAntPowerAction());
        findViewById(R.id.btn_set_test_frequency).setOnClickListener(v -> setTestFrequencyAction());

    }

    private void cwOnAction() {
        final byte toggle = 1;
        final int result = uhfReaderInterface.switchCarrierWave(getReaderId(), toggle);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void cwOffAction() {
        final byte toggle = 0;
        final int result = uhfReaderInterface.switchCarrierWave(getReaderId(), toggle);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void hoppingOnAction() {
        if (Spinner.INVALID_ROW_ID == spinnerHoppingChannel.getSelectedItemId()) {
            showUserMessageShort("请先到“系统配置”中获取一下“发射频率标准”。");
            return;
        }

        final byte toggle = 1;
        final byte channel_index = (byte) spinnerHoppingChannel.getSelectedItemId();
        final int result = uhfReaderInterface.switchHopping(getReaderId(), toggle, channel_index);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void hoppingOffAction() {
        if (Spinner.INVALID_ROW_ID == spinnerHoppingChannel.getSelectedItemId()) {
            showUserMessageShort("请先到“系统配置”中获取一下“发射频率标准”。");
            return;
        }

        final byte toggle = 0;
        final byte channel_index = (byte) spinnerHoppingChannel.getSelectedItemId();
        final int result = uhfReaderInterface.switchHopping(getReaderId(), toggle, channel_index);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void setTestAntPowerAction() {
        if (Spinner.INVALID_ROW_ID == spinnerTestAnt.getSelectedItemId()) {
            showUserMessageShort("请先刷新或获取一下天线配置”。");
        }

        final byte ant = (byte) spinnerTestAnt.getSelectedItemId();
        final int power = Integer.parseInt(spinnerTestPower.getSelectedItem().toString()) * 10;
        final int result = uhfReaderInterface.setTestAntPower(getReaderId(), ant, power);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void setTestFrequencyAction() {
        if (Spinner.INVALID_ROW_ID == spinnerHoppingChannel.getSelectedItemId()) {
            showUserMessageShort("请先到“系统配置”中获取一下“发射频率标准”。");
            return;
        }

        int frequency = (int) (Double.parseDouble(spinnerTestFrequency.getSelectedItem().toString()) * 1000);
        if (transmitFrequencyStandard.equals("Korea") || transmitFrequencyStandard.equals("Europe") || transmitFrequencyStandard.equals("Japan")) {
            frequency |= 0x100000;
        }

        final int result = uhfReaderInterface.setTestFrequency(getReaderId(), frequency);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private boolean validateSetCarrierAdjustTime(final byte[] adjust_time) {
        Integer adjustTime = Utils.tryParseInt(edtCarrierAdjustTime.getText().toString());
        if (adjustTime == null || adjustTime > 255) {
            showUserMessageShort("Adjust Time: 0 ~ 255(ms).");
            return false;
        }

        adjust_time[0] = adjustTime.byteValue();

        return true;
    }

    private boolean validateSetInventoryConfig(final byte[] inventory_algorithm, final byte[] match_report) {
        Integer inventoryAlgorithm = Utils.tryParseInt(edtInventoryConfigAlgorithm.getText().toString());
        Integer matchReport = Utils.tryParseInt(edtInventoryConfigMatchReport.getText().toString());
        if (inventoryAlgorithm != null && inventoryAlgorithm <= 3F && matchReport != null && matchReport <= 255) {
            inventory_algorithm[0] = inventoryAlgorithm.byteValue();
            match_report[0] = matchReport.byteValue();
            return true;
        }

        Toast.makeText(this, "Inventory Algorithm value: 0 ~ 63.\n" +
                "Match Report value: 0 ~ 255.", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void getWorkModeAction() {
        final byte[] work_mode = new byte[1];
        final int result = uhfReaderInterface.getWorkMode(getReaderId(), work_mode);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerWorkMode.setSelection(work_mode[0]);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setWorkModeAction() {
        final byte work_mode = (byte) spinnerWorkMode.getSelectedItemId();
        final int result = uhfReaderInterface.setWorkMode(getReaderId(), work_mode);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getTriggerModeDelayStopInventoryTimeAction() {
        final byte[] time = new byte[1];
        final int result = uhfReaderInterface.getTriggerModeDelayStopReadTime(getReaderId(), time);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtTriggerModeDelayStopInventoryTime.setText(String.valueOf(time[0]));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setTriggerModeDelayStopInventoryTimeAction() {
        if (!validateSetTriggerModeDelayStopInventoryTime()) {
            return;
        }

        final byte time = (byte) Integer.parseInt(edtTriggerModeDelayStopInventoryTime.getText().toString());
        final int result = uhfReaderInterface.setTriggerModeDelayStopReadTime(getReaderId(), time);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetTriggerModeDelayStopInventoryTime() {
        final Integer time = Utils.tryParseInt(edtTriggerModeDelayStopInventoryTime.getText().toString());
        if (time != null && 0 < time && time < 256) {
            return true;
        }

        Toast.makeText(this, "时间大于0小于256。", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void getAutoReadParamsAction() {
        final byte[] bank = new byte[1];
        final short[] offset = new short[1];
        final byte[] length = new byte[1];
        final byte[] password = new byte[4];
        final int result = uhfReaderInterface.getAutoReadParams(getReaderId(), bank, offset, length, password);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerAutoReadBank.setSelection(bank[0]);
            edtAutoReadOffset.setText(String.valueOf(offset[0]));
            edtAutoReadLength.setText(String.valueOf(length[0]));
            edtAutoReadPassword.setText(StringUtils.bytesToHexString(password, 0, 4));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setAutoReadParamsAction() {
        if (!validateSetAutoReadParams()) {
            return;
        }

        final byte bank = (byte) spinnerAutoReadBank.getSelectedItemId();
        final short offset = (short) Integer.parseInt(edtAutoReadOffset.getText().toString());
        final byte length = (byte) Integer.parseInt(edtAutoReadLength.getText().toString());
        final byte[] password = StringUtils.hexStrToBytes(edtAutoReadPassword.getText().toString());
        final int result = uhfReaderInterface.setAutoReadParams(getReaderId(), bank, offset, length, password);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetAutoReadParams() {
        final String str_offset = edtAutoReadOffset.getText().toString();
        final String str_length = edtAutoReadLength.getText().toString();
        final String str_access_password = edtAutoReadPassword.getText().toString();
        if (str_offset.isEmpty() || str_length.isEmpty() || Integer.parseInt(str_length) == 0 || str_access_password.length() != 8) {
            Toast.makeText(this, "偏移、长度不能为空，长度必须大于0，密码为8字节的16进制串。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void setRtcTimeAction() {
        Calendar calendar = Calendar.getInstance();
        final int year = calendar.get(Calendar.YEAR);//获取年份
        final int month = calendar.get(Calendar.MONTH) + 1;//获取月份
        final int day = calendar.get(Calendar.DATE);//获取日
        final int hour = calendar.get(Calendar.HOUR_OF_DAY);//小时
        final int minute = calendar.get(Calendar.MINUTE);//分
        final int second = calendar.get(Calendar.SECOND);//秒
        final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;//一周的第几天
        final byte[] rtc_time = new byte[] {(byte) (year % 2000), (byte) month, (byte) day, (byte) hour, (byte) minute,
                (byte) second, (byte) (dayOfWeek == 0 ? 7 : dayOfWeek) };
        final int result = uhfReaderInterface.setRtcTime(getReaderId(), rtc_time);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getRtcTimeAction() {
        final byte[] rtc_time = new byte[7];
        final int result = uhfReaderInterface.getRtcTime(getReaderId(), rtc_time);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtRtcTime.setText(String.format(Locale.getDefault(), "%d-%02d-%02d %02d:%02d:%02d",
                    (rtc_time[0] & 0xFF) + 2000, rtc_time[1] & 0xFF, rtc_time[2] & 0xFF,
                    rtc_time[3] & 0xFF, rtc_time[4] & 0xFF, rtc_time[5] & 0xFF));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getCommunicationModeAction() {
        final byte[] communication_mode = new byte[1];
        final int result = uhfReaderInterface.getCommunicationMode(getReaderId(), communication_mode);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerCommunicationMode.setSelection(communication_mode[0]);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setCommunicationModeAction() {
        final byte communication_mode = (byte) spinnerCommunicationMode.getSelectedItemId();
        final int result = uhfReaderInterface.setCommunicationMode(getReaderId(), communication_mode);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getRssiFilterAction() {
        final byte[] enable = new byte[1];
        final short[] rssi_value = new short[1];
        final int result = uhfReaderInterface.getRssiFilter(getReaderId(), enable, rssi_value);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            chkRssiFilterEnable.setChecked(enable[0] == 1);
            edtRssiFilterValue.setText(String.format(Locale.getDefault(), "%.1f", -(rssi_value[0] & 0xFFFF) / 10.0));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setRssiFilterAction() {
        if (!validateSetRssiFilter()) {
            return;
        }

        final byte enable = (byte) (chkRssiFilterEnable.isChecked() ? 1 : 0);
        final short rssi_value = (short) (-(int)Double.parseDouble(edtRssiFilterValue.getText().toString()) * 10);
        final int result = uhfReaderInterface.setRssiFilter(getReaderId(), enable, rssi_value);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetRssiFilter() {
        try {
            final int rssi_value = -(int)(Double.parseDouble(edtRssiFilterValue.getText().toString()) * 10);
            if (0 < rssi_value && rssi_value < 0xFFFF) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Toast.makeText(this, "RSSI值小于0大于-65535", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void getTagFilterAction() {
        final byte[] enable = new byte[1];
        final byte[] time = new byte[1];
        final int result = uhfReaderInterface.getTagFilter(getReaderId(), enable, time);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            chkTagFilterEnable.setChecked(enable[0] == 1);
            edtTagFilterTime.setText(String.valueOf(time[0] & 0xFF));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setTagFilterAction() {
        if (!validateSetTagFilter()) {
            return;
        }

        final byte enable = (byte) (chkTagFilterEnable.isChecked() ? 1 : 0);
        final byte time = (byte) Integer.parseInt(edtTagFilterTime.getText().toString());
        final int result = uhfReaderInterface.setTagFilter(getReaderId(), enable, time);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetTagFilter() {
        final Integer time = Utils.tryParseInt(edtTagFilterTime.getText().toString());
        if (time != null && 0 < time && time < 0xFF) {
            return true;
        }

        Toast.makeText(this, "过滤时间取值：1 ~ 255。", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void setBuzzerEnableAction() {
        final byte enable = (byte) (chkBuzzerEnable.isChecked() ? 1 : 0);
        final int result = uhfReaderInterface.setBuzzerEnable(getReaderId(), enable);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getBuzzerEnableAction() {
        final byte[] enable = new byte[1];
        final int result = uhfReaderInterface.getBuzzerEnable(getReaderId(), enable);
        if (EraLinkUHFReaderApiError.ErrNoError == result){
            chkBuzzerEnable.setChecked(enable[0] == 1);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getBaudAction() {
        final int[] baud = new int[1];
        final int result = uhfReaderInterface.getBaud(getReaderId(), baud);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            if (BAUD_INDEX_MAP.containsKey(baud[0])) {
                spinnerBaud.setSelection(BAUD_INDEX_MAP.get(baud[0]));
            }
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setBaudAction() {
        final int baud = Integer.parseInt(spinnerBaud.getSelectedItem().toString());
        final int result = uhfReaderInterface.setBaud(getReaderId(), baud);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getRs485AddressAction() {
        final byte[] rs485_address = new byte[1];
        final int result = uhfReaderInterface.getRs485Address(getReaderId(), rs485_address);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtRs485Address.setText(String.valueOf(rs485_address[0] & 0xFF));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setRs485AddressAction() {
        final byte[] rs485_address = new byte[1];
        if (!validateSetRs485Address(rs485_address)) {
            return;
        }

        final int result = uhfReaderInterface.setRs485Address(getReaderId(), rs485_address[0]);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetRs485Address(final byte[] rs485_address) {
        final Integer address = Utils.tryParseInt(edtRs485Address.getText().toString());
        if (address != null && 0 <= address && address <= 0xFE) {
            rs485_address[0] = address.byteValue();
            return true;
        }

        Toast.makeText(this, "RS485地址取值：0 ～ 254。", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void clearWifiParamsAction() {
        chkWifiIPItem.setChecked(false);
        spinnerWifiIPMode.setSelection(0);
        edtWifiIP.setText("");
        edtWifiIPMask.setText("");
        edtWifiIPGateway.setText("");
        edtWifiIPDns.setText("");

        chkWifiAPItem.setChecked(false);
        edtWifiAPSsid.setText("");
        edtWifiAPKey.setText("");

        chkWifiSocketItem.setChecked(false);
        spinnerWifiSocketMode.setSelection(0);
        edtWifiSocketIP.setText("");
        edtWifiSocketPort.setText("");

        chkWifiMacItem.setChecked(false);
        edtWifiMac.setText("");
    }

    private void getWifiParamsAction() {
        if (!chkWifiIPItem.isChecked() && !chkWifiAPItem.isChecked() && !chkWifiSocketItem.isChecked() &&
                !chkWifiMacItem.isChecked()) {
            Toast.makeText(this, "请至少指定一个WIFI配置项。", Toast.LENGTH_SHORT).show();
            return;
        }

        showUserDialogNoCancel("获取WIFI参数中。。。");
        GetWifiParamsTask task = new GetWifiParamsTask(this);
        task.execute(chkWifiIPItem.isChecked() ? 1 : 0, chkWifiAPItem.isChecked() ? 1 : 0,
                chkWifiSocketItem.isChecked() ? 1 : 0, chkWifiMacItem.isChecked() ? 1 : 0);
    }

    private void setWifiParamsAction() {
        chkWifiMacItem.setChecked(false);

        int items = 0;
        if (chkWifiIPItem.isChecked()) {
            items += 1;
        }
        if (chkWifiAPItem.isChecked()) {
            items += 1;
        }
        if (chkWifiSocketItem.isChecked()) {
            items += 1;
        }

        if (items == 0 || items > 1) {
            Toast.makeText(this, "指定一个WIFI配置项。", Toast.LENGTH_SHORT).show();
            return;
        }

        final String[] params = new String[3];
        if (chkWifiIPItem.isChecked()) {
            if (!validateSetWifiIPParams()) {
                return;
            }
            if (spinnerWifiIPMode.getSelectedItemId() == 1) {
                params[0] = "DHCP";
            }
            else {
                params[0] = String.format("STATIC,%s,%s,%s,%s", edtWifiIP.getText().toString(), edtWifiIPMask.getText().toString(),
                        edtWifiIPGateway.getText().toString(), edtWifiIPDns.getText().toString());
            }
        }
        if (chkWifiAPItem.isChecked()) {
            if (!validateSetWifiAPParams()) {
                return;
            }
            params[1] = String.format("%s,%s", edtWifiAPSsid.getText().toString(), edtWifiAPKey.getText().toString());
        }
        if (chkWifiSocketItem.isChecked()) {
            if (!validateSetWifiSocketParams()) {
                return;
            }
            params[2] = String.format("%s,%s,%s", spinnerWifiSocketMode.getSelectedItemId() == 0 ? "TCPC" : "TCPS",
                    edtWifiSocketIP.getText().toString(), edtWifiSocketPort.getText().toString());
        }

        showUserDialogNoCancel("设置WIFI参数中。。。");
        SetWifiParamsTask task = new SetWifiParamsTask(this);
        task.execute(params);
    }

    private boolean validateSetWifiIPParams() {
        if (spinnerWifiIPMode.getSelectedItemId() == 1) {
            return true;
        }

        if (!Utils.ipCheck(edtWifiIP.getText().toString().trim()) ||
                !Utils.ipCheck(edtWifiIPMask.getText().toString().trim()) ||
                !Utils.ipCheck(edtWifiIPGateway.getText().toString().trim()) ||
                !Utils.ipCheck(edtWifiIPDns.getText().toString().trim())) {
            Toast.makeText(this, "WIFI IP项的IP、掩码、网关、DNS必须为合法的IP地址。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private boolean validateSetWifiAPParams() {
        final String ssid = edtWifiAPSsid.getText().toString();
        final String key = edtWifiAPKey.getText().toString();
        if (ssid.isEmpty() || ssid.indexOf(',') != -1 || ssid.indexOf(';') != -1 ||
                key.isEmpty() || key.indexOf(',') != -1 || key.indexOf(';') != -1) {
            Toast.makeText(this, "WIFI AP项的SSID或KEY不能为空且最大长度为32个可见英文字符并且不能包含逗号和分号。", Toast.LENGTH_SHORT).show();
        }

        return true;
    }

    private boolean validateSetWifiSocketParams() {
        if (!Utils.ipCheck(edtWifiSocketIP.getText().toString())) {
            Toast.makeText(this, "WIFI Socket项的IP非法。", Toast.LENGTH_SHORT).show();
            return false;
        }

        final Integer port = Utils.tryParseInt(edtWifiSocketPort.getText().toString());
        if (port == null || 0 > port || 65535 < port) {
            Toast.makeText(this, "WIFI Socket项的端口取值： 0 ～ 65535。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void clearGprsParamsAction() {
        chkGprsSocketItem.setChecked(false);
        spinnerGprsSocketProtocol.setSelection(0);
        edtGprsSocketIP.setText("");
        edtGprsSocketPort.setText("");

        chkGprsAPNItem.setChecked(false);
        edtGprsApn.setText("");
        spinnerGprsAuth.setSelection(0);
        edtGprsApnAccount.setText("");
        edtGprsApnPassword.setText("");
    }

    private void getGprsParamsAction() {
        if (!chkGprsSocketItem.isChecked() && !chkGprsAPNItem.isChecked()) {
            Toast.makeText(this, "请至少指定一个GPRS配置项。", Toast.LENGTH_SHORT).show();
        }

        showUserDialogNoCancel("获取GPRS参数中。。。");
        GetGprsParamsTask task = new GetGprsParamsTask(this);
        task.execute(chkGprsSocketItem.isChecked()? 1 : 0, chkGprsAPNItem.isChecked()? 1 : 0);
    }

    private void setGprsParamsAction() {
        if ((!chkGprsSocketItem.isChecked() && !chkGprsAPNItem.isChecked()) ||
                (chkGprsSocketItem.isChecked() && chkGprsAPNItem.isChecked())) {
            Toast.makeText(this, "请指定一个GPRS配置项。", Toast.LENGTH_SHORT).show();
            return;
        }

        final String[] params = new String[2];
        if (chkGprsSocketItem.isChecked()) {
            if (!validateSetGprsSocketParams()) {
                return;
            }
            params[0] = String.format("%s,%s,%s", spinnerGprsSocketProtocol.getSelectedItem().toString(),
                    edtGprsSocketIP.getText().toString(), edtGprsSocketPort.getText().toString());
        }

        if (chkGprsAPNItem.isChecked()) {
            if (!validateSetGprsAPNParams()) {
                return;
            }

            params[1] = String.format("%s,%s,%s,%s", edtGprsApn.getText().toString(),
                    edtGprsApnAccount.getText().toString(), edtGprsApnPassword.getText().toString(),
                    spinnerGprsAuth.getSelectedItemId());
        }

        showUserDialogNoCancel("设置GPRS参数中。。。");
        SetGprsParamsTask task = new SetGprsParamsTask(this);
        task.execute(params);
    }

    private boolean validateSetGprsSocketParams() {
        if (!Utils.ipCheck(edtGprsSocketIP.getText().toString())) {
            Toast.makeText(this, "GPRS Socket项的IP非法。", Toast.LENGTH_SHORT).show();
            return false;
        }

        final Integer port = Utils.tryParseInt(edtGprsSocketPort.getText().toString());
        if (port == null || 0 > port || 65535 < port) {
            Toast.makeText(this, "GPRS Socket项的端口取值： 0 ～ 65535。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private boolean validateSetGprsAPNParams() {
        final String apn = edtGprsApn.getText().toString();
        final String account = edtGprsApnAccount.getText().toString();
        final String password = edtGprsApnPassword.getText().toString();
        if (apn.indexOf(',') != -1 || apn.indexOf(';') != -1 ||
                account.indexOf(',') != -1 || account.indexOf(';') != -1 ||
                password.indexOf(',') != -1 || password.indexOf(';') != -1) {
            Toast.makeText(this, "GPRS APN项的APN、Account、Password不能为空且最大长度为32个可见英文字符并且不能包含逗号和分号。", Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private void getWgParamsAction() {
        final byte[] protocol_type = new byte[1];
        final byte[] data_start_address = new byte[1];
        final byte[] pulse_width = new byte[1];
        final byte[] pulse_cycle = new byte[1];
        final byte[] data_interval = new byte[1];
        final int result = uhfReaderInterface.getWgParams(
                getReaderId(), protocol_type, data_start_address, pulse_width, pulse_cycle, data_interval);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            spinnerWgProtocolFormat.setSelection(protocol_type[0]);
            edtWgDataStartAddress.setText(String.valueOf(data_start_address[0] & 0xFF));
            edtWgPulseWidth.setText(String.valueOf(pulse_width[0] & 0xFF));
            edtWgPulseCycle.setText(String.valueOf(pulse_cycle[0] & 0xFF));
            edtWgDataInterval.setText(String.valueOf(data_interval[0]));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setWgParamsAction() {
        if (!validateSetWgParams()) {
            return;
        }

        final byte protocol_type = (byte) spinnerWgProtocolFormat.getSelectedItemId();
        final byte data_start_address = (byte) Integer.parseInt(edtWgDataStartAddress.getText().toString());
        final byte pulse_width = (byte) Integer.parseInt(edtWgPulseWidth.getText().toString());
        final byte pulse_cycle = (byte) Integer.parseInt(edtWgPulseCycle.getText().toString());
        final byte data_interval = (byte) Integer.parseInt(edtWgDataInterval.getText().toString());
        final int result = uhfReaderInterface.setWgParams(
                getReaderId(), protocol_type, data_start_address, pulse_width, pulse_cycle, data_interval);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private boolean validateSetWgParams() {
        final Integer data_start_address = Utils.tryParseInt(edtWgDataStartAddress.getText().toString());
        final Integer pulse_width = Utils.tryParseInt(edtWgPulseWidth.getText().toString());
        final Integer pulse_cycle = Utils.tryParseInt(edtWgPulseCycle.getText().toString());
        final Integer data_interval = Utils.tryParseInt(edtWgDataInterval.getText().toString());
        if (data_start_address != null && 0 <= data_start_address && data_start_address <= 9 &&
                pulse_width != null && 1 <= pulse_width && pulse_width <= 255 &&
                pulse_cycle != null && 1 <= pulse_cycle && pulse_cycle <= 255 &&
                data_interval != null && 1 <= data_interval && data_interval <= 255) {
            return true;
        }

        Toast.makeText(this, "韦根数据起始地址：0 ~ 9。\n" +
                "韦根脉冲宽度：1 ~ 255。\n" +
                "韦根脉冲周期：1 ~ 255。\n" +
                "韦根数据间隔：1 ~ 255。", Toast.LENGTH_SHORT).show();

        return false;
    }

    private void getIOInputStateAction() {
        final byte[] statuses = new byte[2];
        final int[] statuses_len = new int[]{ statuses.length };
        final int result = uhfReaderInterface.getGpioTriggerStatus(getReaderId(), statuses, statuses_len);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            chkIoInput1State.setChecked(statuses[0] == 1);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setRelay1StateAction() {
        final byte relay_no = 1;
        final byte relay_status = (byte) (chkRelay1State.isChecked() ? 1 : 0);
        final int result = uhfReaderInterface.setRelayStatus(getReaderId(), relay_no, relay_status);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setRelay2StateAction() {
        final byte relay_no = 2;
        final byte relay_status = (byte) (chkRelay2State.isChecked() ? 1 : 0);
        final int result = uhfReaderInterface.setRelayStatus(getReaderId(), relay_no, relay_status);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getTransmitFrequencyStandardAction() {
        final int[] value = new int[1];
        int result = uhfReaderInterface.readOemRegister(getReaderId(), 0x000000BD, value);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            transmitFrequencyStandard = "Unknown";
            if (0x00180E61 == value[0]) {// 国标
                transmitFrequencyStandard = "China";
            } else if (0x003C23D5 == value[0]) { // 韩标
                transmitFrequencyStandard = "Korea";
            } else if (0x003C21D1 == value[0]) {// 欧标
                transmitFrequencyStandard = "Europe";
            } else if (0x003C23D0 == value[0]) {// 日本
                transmitFrequencyStandard = "Japan";
            } else if (0x00180E1B == value[0]) { // 美标
                transmitFrequencyStandard = "FCC";
            } else if (value[0] == 0x00000000) {
                result = uhfReaderInterface.readOemRegister(getReaderId(), 0x0000009D, value);
                if (EraLinkUHFReaderApiError.ErrNoError == result) {
                    if (0x00000000 == value[0]) { // 美标
                        transmitFrequencyStandard = "FCC";
                    } else if (0x00000001 == value[0]) { // 欧标
                        transmitFrequencyStandard = "Europe";
                    } else if (0x00000002 == value[0]) { // 日本
                        transmitFrequencyStandard = "Japan";
                    }

                    edtTransmitFrequencyStandard.setText(transmitFrequencyStandard);
                }
                Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
                return;
            }

            edtTransmitFrequencyStandard.setText(transmitFrequencyStandard);

            if (channelNumberToFrequencyCfg == null) {
                showUserDialogNoCancel("加载测试配置中。。。");
                LoadTestConfigTask task = new LoadTestConfigTask(this);
                task.execute();
            }
        }

        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getFirmwareVersionAction() {
        final String[] version = new String[1];
        final int result = uhfReaderInterface.getFirmwareVersion(getReaderId(), version);
        if (EralinkApiErrDefinition.ErrNoError == result) {
            edtFirmwareVersion.setText(version[0]);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getDeviceNameAction() {
        final String[] device_name = new String[1];
        final int result = uhfReaderInterface.getDeviceName(getReaderId(), device_name);
        if (EralinkApiErrDefinition.ErrNoError == result) {
            edtDeviceName.setText(device_name[0]);
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void setDeviceNameAction() {
        final String device_name = edtDeviceName.getText().toString().trim();
        if (device_name.isEmpty()) {
            return;
        }
        final int result = uhfReaderInterface.setDeviceName(getReaderId(), device_name);
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void selectUpgradeFileAction() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        startActivityForResult(intent, REQUEST_CODE_FOR_UPGRADE_FILE);
    }

    private void upgradeAction() {
        if (upgradeFileUri == null) {
            Toast.makeText(this, "请选择升级文件。", Toast.LENGTH_SHORT).show();
            return;
        }

        showUserDialogNoCancel("固件升级中。。。");
        UpgradeTask task = new UpgradeTask(this);
        task.execute();
    }

    private void getAllSystemParamsAction() {
        final byte[] system_params = new byte[512];
        final int[] system_params_len = new int[]{ system_params.length };
        final int result = uhfReaderInterface.getAllSystemParams(getReaderId(), system_params, system_params_len);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtAllSystemParams.setText(StringUtils.bytesToHexString(system_params, 0, system_params_len[0]));
        }
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getReaderStateAction() {
        final int result = uhfReaderInterface.getReaderState(getReaderId());
        Toast.makeText(this, getResources().getString(ErrString.getErrId(result)), Toast.LENGTH_SHORT).show();
    }

    private void getSNAction() {
        final String[] sn = new String[1];
        final int result = uhfReaderInterface.getSN(getReaderId(), sn);
        if (EraLinkUHFReaderApiError.ErrNoError == result) {
            edtSN.setText(sn[0]);
        }
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void setHeartbeatIntervalAction() {
        final byte[] interval = new byte[1];
        if (!validateSetHeartbeatInterval(interval)) {
            return;
        }

        final int result = uhfReaderInterface.setHeartbeatInterval(getReaderId(), interval[0]);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private boolean validateSetHeartbeatInterval(final byte[] heartbeatInterval) {
        Integer interval = Utils.tryParseInt(edtHeartbeatInterval.getText().toString());
        if (interval == null || interval < 1 || interval > 255) {
            showUserMessageShort("心跳间隔取值： 1 ～ 255秒。");
            return false;
        }

        heartbeatInterval[0] = interval.byteValue();

        return true;
    }

    private void sendHeartbeatAction() {
        final int result = uhfReaderInterface.heartbeat(getReaderId());
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void resetUHFAction() {
        final int result = uhfReaderInterface.resetUHF(getReaderId());
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void resetReaderAction() {
        final int result = uhfReaderInterface.resetReader(getReaderId());
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private void setReaderMacAction() {
        if (!validateSetReaderMac()) {
            return;
        }

        StringBuilder builder = new StringBuilder();
        for (int index = 0; index < edtMacAddressGroup.length; ++index) {
            if (index == 2) {
                if (spinnerProductType.getSelectedItemId() == 0) {
                    builder.append(5);
                } else {
                    builder.append(0);
                }
            } else {
                builder.append(edtMacAddressGroup[index].getText().toString());
            }
        }

        final byte[] mac = StringUtils.hexStrToBytes(builder.toString());
        final int result = uhfReaderInterface.setMacAddress(getReaderId(), mac);
        showUserMessageShort(getResources().getString(ErrString.getErrId(result)));
    }

    private boolean validateSetReaderMac() {
        for (int index = 0; index < edtMacAddressGroup.length; ++index) {
            if (index == 2) {
                continue;
            }

            if (edtMacAddressGroup[index].getText().toString().isEmpty()) {
                showUserMessageShort("MAC地址的字符不能为空。");
                return false;
            }
        }

        return true;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data == null) {
            return;
        }
        if (resultCode != Activity.RESULT_OK) {
            return;
        }

        if (requestCode == REQUEST_CODE_FOR_SEARCH_READER) {
            String reader = data.getStringExtra("reader");
            if (reader != null) {
                String[] fields = reader.split(":");
                edtReaderIP.setText(fields[0]);
                edtReaderPort.setText(fields[1]);
            }
        } else {
            Uri uri = data.getData();
            try (Cursor cursor = getContentResolver().query(uri, null, null, null, null, null)) {
                if (cursor != null && cursor.moveToFirst()) {
                    String display_name = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                    if (requestCode == REQUEST_CODE_FOR_FREQUENCY_FILE) {
                        frequencyBandFileUri = uri;
                        edtFrequencyBandFilePath.setText(display_name);
                    } else if (requestCode == REQUEST_CODE_FOR_UPGRADE_FILE) {
                        upgradeFileUri = uri;
                        edtUpgradeFilePath.setText(display_name);
                    }
                }
            }
        }
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            onBackPressed();
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        timerForCurrentTime.cancel();
        if (uhfReaderInterface != null) {
            uhfReaderInterface.close();
            uhfReaderInterface = null;
        }
        super.onDestroy();
    }

    public void showUserDialogNoCancel(String message) {
        if (dialog == null) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            View view = getLayoutInflater().inflate(R.layout.dialog_doing, null);
            tvMessage = view.findViewById(R.id.tv_message);
            tvMessage.setText(message);
            builder.setView(view);
            dialog = builder.show();
            dialog.setCancelable(false);
        } else {
            tvMessage.setText(message);
        }
    }

    public void showUserDialog(String message) {
        if (dialog == null) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            View view = getLayoutInflater().inflate(R.layout.dialog_doing, null);
            tvMessage = view.findViewById(R.id.tv_message);
            tvMessage.setText(message);
            builder.setView(view);
            dialog = builder.show();
        } else {
            tvMessage.setText(message);
        }
    }

    public void hideUserDialog() {
        if (dialog != null) {
            dialog.dismiss();
            dialog = null;
        }
    }

    public void showUserMessageShort(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    private static class UpdateFrequencyBandTask extends AsyncTask<Void, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;
        UpdateFrequencyBandTask(MainActivity mainActivity) {
            this.mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Void... voids) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return EraLinkUHFReaderApiError.ErrRequestFailed;
            }

            List<FrequencyPoint> frequencyPoints = new ArrayList<>();
            if (!loadFrequencyFile(frequencyPoints)) {
                return EraLinkUHFReaderApiError.ErrBadArg;
            }

            int line = 0;
            for (FrequencyPoint fp : frequencyPoints) {
                System.out.printf("line: %d, address: 0x%08x value: 0x%08x\n", ++line, fp.getAddress(), fp.getValue());
                final int result = mainActivity.uhfReaderInterface.writeOemRegisterWithoutReset(mainActivity.getReaderId(), fp.getAddress(), fp.getValue());
                if (EralinkApiErrDefinition.ErrNoError != result) {
                    return result;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            return mainActivity.uhfReaderInterface.resetUHF((byte) 0);
        }

        private boolean loadFrequencyFile(List<FrequencyPoint> frequencyPoints) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null || mainActivity.frequencyBandFileUri == null) {
                return false;
            }

            try {
                InputStream is = mainActivity.getContentResolver().openInputStream(mainActivity.frequencyBandFileUri);
                BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                int count = 0;
                while (true) {
                    final String line = reader.readLine();
                    if (line == null) {
                        break;
                    }

                    count += 1;
                    // System.out.printf("frequency band file line: %s, %s\n", count, line);

                    final String[] fields = line.split(",");
                    if (fields.length != 2) {
                        return false;
                    }
                    final Integer address = Utils.tryParseHexInt(fields[0]);
                    final Integer value = Utils.tryParseHexInt(fields[1]);
                    if (address == null || value == null) {
                        return false;
                    }

                    frequencyPoints.add(new FrequencyPoint(address, value));
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

            return true;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                if (EraLinkUHFReaderApiError.ErrNoError == result) {
                    mainActivity.showUserMessageShort("更新频段成功。");
                } else {
                    mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
                }
            }
        }

        static class FrequencyPoint {
            private int address;
            private int value;
            FrequencyPoint(int address, int value) {
                this.setAddress(address);
                this.setValue(value);
            }

            public int getAddress() {
                return address;
            }

            public void setAddress(int address) {
                this.address = address;
            }

            public int getValue() {
                return value;
            }

            public void setValue(int value) {
                this.value = value;
            }

            @Override
            public String toString() {
                return "FrequencyPoint{" +
                        "address=" + address +
                        ", value=" + value +
                        '}';
            }
        }
    }

    private static class GetWifiParamsTask extends AsyncTask<Integer, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;
        final String[] params = new String[1];

        GetWifiParamsTask(MainActivity mainActivity) {
            mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Integer... values) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            if (values.length != 4) {
                return null;
            }

            // 0，ip；1，ap；2，sock；3，mac（MAC地址不能修改）
            for (int index = 0; index < 4; ++index) {
                if (values[index] != 0) {
                    final int result = mainActivity.uhfReaderInterface.getWifiParams(mainActivity.getReaderId(), (byte) index, params);
                    if (EraLinkUHFReaderApiError.ErrNoError != result) {
                        return result;
                    }

                    publishProgress(25 * (index + 1));
                }
            }

            return EraLinkUHFReaderApiError.ErrNoError;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            if (values[0] == 25) {
                final String[] fields = params[0].split(",");
                final String mode = fields[0].toUpperCase();
                if (mode.equals("DHCP") || mode.equals("STATIC")) {
                    mainActivity.spinnerWifiIPMode.setSelection(mode.equals("STATIC") ? 0 : 1);
                    mainActivity.edtWifiIP.setText(fields[1]);
                    mainActivity.edtWifiIPMask.setText(fields[2]);
                    mainActivity.edtWifiIPGateway.setText(fields[3]);
                    mainActivity.edtWifiIPDns.setText(fields[4]);
                }
            } else if (values[0] == 50) {
                final String[] fields = params[0].split(",");
                mainActivity.edtWifiAPSsid.setText(fields[0]);
                mainActivity.edtWifiAPKey.setText(fields[1]);
            } else if (values[0] == 75) {
                final String[] fields = params[0].split(",");
                final String protocol = fields[0].toUpperCase();
                if (protocol.equals("TCPS") || protocol.equals("TCPC")) {
                    mainActivity.spinnerWifiSocketMode.setSelection(protocol.equals("TCPC") ? 0 : 1);
                    mainActivity.edtWifiSocketIP.setText(fields[1]);
                    mainActivity.edtWifiSocketPort.setText(fields[2]);
                }
            } else if (values[0] == 100) {
                mainActivity.edtWifiMac.setText(params[0]);
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
            }
        }
    }

    private static class SetWifiParamsTask extends AsyncTask<String, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        SetWifiParamsTask(MainActivity mainActivity) {
            mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(String... values) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            if (values.length != 3) {
                return null;
            }

            // 0，ip；1，ap；2，sock；3，mac（MAC地址不能修改）
            for (int index = 0; index < 3; ++index) {
                if (values[index] != null) {
                    final int result = mainActivity.uhfReaderInterface.setWifiParams(mainActivity.getReaderId(), (byte) index, values[index]);
                    if (EraLinkUHFReaderApiError.ErrNoError != result) {
                        return result;
                    }
                    break;
                }
            }

            return null;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
            }
        }
    }

    private static class GetGprsParamsTask extends AsyncTask<Integer, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;
        final String[] params = new String[1];

        GetGprsParamsTask(MainActivity mainActivity) {
            mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Integer... values) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            if (values.length != 2) {
                return null;
            }

            // 0，socket；1，apn
            if (values[0] == 1) {
                final int result = mainActivity.uhfReaderInterface.getGprsParams(mainActivity.getReaderId(), (byte) 0, params);
                if (EraLinkUHFReaderApiError.ErrNoError != result) {
                    return result;
                }
                publishProgress(50);
            }

            if (values[1] == 1) {
                final int result = mainActivity.uhfReaderInterface.getGprsParams(mainActivity.getReaderId(), (byte) 1, params);
                if (EraLinkUHFReaderApiError.ErrNoError != result) {
                    return result;
                }
                publishProgress(100);
            }

            return EraLinkUHFReaderApiError.ErrNoError;
        }


        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            if (values[0] == 50) {
                final String[] fields = params[0].split(",");
                final String protocol = fields[0].toUpperCase();
                if (protocol.equals("TCP") || protocol.equals("UDP")) {
                    mainActivity.spinnerGprsSocketProtocol.setSelection(protocol.equals("TCP") ? 0 : 1);
                    mainActivity.edtGprsSocketIP.setText(fields[1]);
                    mainActivity.edtGprsSocketPort.setText(fields[2]);
                }
            } else if (values[0] == 100) {
                final String[] fields = params[0].split(",");
                mainActivity.edtGprsApn.setText(fields[0]);
                mainActivity.edtGprsApnAccount.setText(fields[1]);
                mainActivity.edtGprsApnPassword.setText(fields[2]);
                mainActivity.spinnerGprsAuth.setSelection(Integer.parseInt(fields[3]));
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
            }
        }
    }

    private static class SetGprsParamsTask extends AsyncTask<String, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        SetGprsParamsTask(MainActivity mainActivity) {
            mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(String... values) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            if (values.length != 2) {
                return null;
            }

            // 0，socket；1，apn
            if (values[0] != null) {
                final int result = mainActivity.uhfReaderInterface.setGprsParams(mainActivity.getReaderId(), (byte) 0, values[0]);
                if (EraLinkUHFReaderApiError.ErrNoError != result) {
                    return result;
                }
            }

            if (values[1] != null) {
                final int result = mainActivity.uhfReaderInterface.setGprsParams(mainActivity.getReaderId(), (byte) 1, values[1]);
                if (EraLinkUHFReaderApiError.ErrNoError != result) {
                    return result;
                }
            }

            return EraLinkUHFReaderApiError.ErrNoError;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
            }
        }
    }

    private static class UpgradeTask extends AsyncTask<Void, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;
        UpgradeTask(MainActivity mainActivity) {
            this.mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Void... voids) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            try {
                InputStream is = mainActivity.getContentResolver().openInputStream(mainActivity.upgradeFileUri);
                final int file_size = is.available();
                final byte[] file_content = new byte[file_size];
                is.read(file_content);
                is.close();

                int batch = 0;
                final int BLOCK_SIZE = 512;
                int sequence = 0;
                byte[] block = new byte[513];
                for (; batch < (file_size / BLOCK_SIZE); ++batch) {
                    block[0] = (byte) sequence++;
                    System.arraycopy(file_content, batch * BLOCK_SIZE, block, 1, BLOCK_SIZE);
                    System.out.printf("传输升级文件第 %d 块: %s\n", sequence, StringUtils.bytesToHexString(block, 0, block.length));
                    final int result = mainActivity.uhfReaderInterface.upgrade(mainActivity.getReaderId(), block);
                    if (EralinkApiErrDefinition.ErrNoError != result) {
                        return result;
                    }

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                final int left_file_size = file_size - batch * BLOCK_SIZE;
                if (0 < left_file_size) {
                    block = new byte[1 + left_file_size];
                    block[0] = (byte) sequence++;
                    System.arraycopy(file_content, batch * BLOCK_SIZE, block, 1, left_file_size);
                    System.out.printf("传输升级文件第 %d 块: %s\n", sequence, StringUtils.bytesToHexString(block, 0, block.length));
                    final int result = mainActivity.uhfReaderInterface.upgrade(mainActivity.getReaderId(), block);
                    if (EralinkApiErrDefinition.ErrNoError != result) {
                        return result;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return EraLinkUHFReaderApiError.ErrNoError;
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                if (EraLinkUHFReaderApiError.ErrNoError == result) {
                    mainActivity.showUserMessageShort("固件升级成功。");
                } else {
                    mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
                }
            }
        }
    }

    private static class RefreshAntsTask extends AsyncTask<Void, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        final byte[] ant_echo_detecting_params = new byte[256];
        final int[] ant_echo_detecting_params_len = new int[]{ 256 };
        private int a1, a0;
        RefreshAntsTask(MainActivity mainActivity) {
            this.mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Void... voids) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            int result = getAntConfig();
            if (EraLinkUHFReaderApiError.ErrNoError != result) {
                return result;
            }

            publishProgress(30);

            result = mainActivity.uhfReaderInterface.getAntEchoDetectingParams(mainActivity.getReaderId(), ant_echo_detecting_params, ant_echo_detecting_params_len);
            if (EraLinkUHFReaderApiError.ErrNoError != result) {
                return result;
            }

            int registerAddress = 0x000003C7;
            final int[] registerValue = new int[1];
            result = mainActivity.uhfReaderInterface.readOemRegister(mainActivity.getReaderId(), registerAddress, registerValue);
            if (EraLinkUHFReaderApiError.ErrNoError != result) {
                return result;
            }
            a1 = registerValue[0];

            registerAddress = 0x000003C8;
            result = mainActivity.uhfReaderInterface.readOemRegister(mainActivity.getReaderId(), registerAddress, registerValue);
            if (EraLinkUHFReaderApiError.ErrNoError != result) {
                return result;
            }
            a0 = registerValue[0];

            publishProgress(70);

            final byte[] antConfig = new byte[12 * mainActivity.ant_num];
            System.arraycopy(mainActivity.ant_config, 0, antConfig, 0, antConfig.length);
            result = mainActivity.uhfReaderInterface.setAntConfig(mainActivity.getReaderId(), antConfig);
            if (EraLinkUHFReaderApiError.ErrNoError != result) {
                return result;
            }

            publishProgress(100);

            return EraLinkUHFReaderApiError.ErrNoError;
        }

        private int getAntConfig() {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return EraLinkUHFReaderApiError.ErrRequestFailed;
            }

            return mainActivity.uhfReaderInterface.getAntConfig(mainActivity.getReaderId(), mainActivity.ant_config, mainActivity.ant_config_len);
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            if (30 == values[0]) { // 更新天线显示
                mainActivity.ant_num = mainActivity.ant_config_len[0] / 12;
                for (int index = 1; index < mainActivity.ant_num; ++index) {
                    mainActivity.llAntConfigs[index].setVisibility(View.VISIBLE);
                }
                for (int index = mainActivity.ant_num; index < mainActivity.llAntConfigs.length; ++index) {
                    mainActivity.llAntConfigs[index].setVisibility(View.GONE);
                }

                mainActivity.testAntList.clear();
                for (int index = 0; index < mainActivity.ant_num; ++index) {
                    final int enable = Utils.u32FromLittleEndianU8s(mainActivity.ant_config, index * 12);
                    final int dwellTime = Utils.u32FromLittleEndianU8s(mainActivity.ant_config, index * 12 + 4);
                    final int power = Utils.u32FromLittleEndianU8s(mainActivity.ant_config, index * 12 + 8) / 10;
                    mainActivity.chkAntEnableGroup[index].setEnabled(true);
                    mainActivity.chkAntEnableGroup[index].setChecked(enable == 1);
                    Integer selectIndex = mainActivity.dwellTimes.get(dwellTime);
                    if (selectIndex != null) {
                        mainActivity.spinnerAntDwellTimeGroup[index].setSelection(selectIndex);
                    }
                    selectIndex = mainActivity.outputPowers.get(power);
                    if (selectIndex != null) {
                        mainActivity.spinnerAntOutputPowerGroup[index].setSelection(selectIndex);
                    }

                    mainActivity.testAntList.add(index + 1);
                }
                mainActivity.testAntListAdapter.notifyDataSetChanged();
            } else if (70 == values[0]) {
                final double divisor = Math.pow(2, 24);
                for (int index = 0; index < mainActivity.ant_num; ++index) {
                    final int antRevAdc = Utils.u32FromLittleEndianU8s(ant_echo_detecting_params, index * 4);
                    final double target = (a1 / divisor) * antRevAdc + (a0 / divisor);
                    final int difference = (int) Math.round(Math.abs(target - 30));
                    mainActivity.chkAntEnableGroup[index].setEnabled(difference >= BACKWARDPOWERTHRESHOLD);
                    if (!mainActivity.chkAntEnableGroup[index].isEnabled()) {
                        mainActivity.chkAntEnableGroup[index].setChecked(false);
                    } else {
                        mainActivity.chkAntEnableGroup[index].setChecked(true);
                    }
                    mainActivity.tvAntReversePowerDifferenceGroup[index].setText(String.valueOf(difference));

                    final int enable = (mainActivity.chkAntEnableGroup[index].isEnabled() && mainActivity.chkAntEnableGroup[index].isChecked()) ? 1 : 0;
                    Utils.u32ToLittleEndianU8s(enable, mainActivity.ant_config, index * 12);
                }
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                if (EraLinkUHFReaderApiError.ErrNoError == result) {
                    mainActivity.showUserMessageShort("天线刷新成功。");
                } else {
                    mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
                }
            }
        }
    }

    private static class LoadTestConfigTask extends AsyncTask<Void, Integer, Integer> {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        LoadTestConfigTask(MainActivity mainActivity) {
            this.mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        protected Integer doInBackground(Void... voids) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return null;
            }

            File testConfigFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Cfg/channelNumberToFrequency.cfg");
            System.out.printf("test config file path: %s\n", testConfigFile.getAbsolutePath());
            if (!testConfigFile.exists()) {
                return EraLinkUHFReaderApiError.ErrBadArg;
            }

            byte[] file_content = null;
            try {
                InputStream is = new FileInputStream(testConfigFile);
                final int file_size = is.available();
                file_content = new byte[file_size];
                is.read(file_content);
                is.close();

                mainActivity.channelNumberToFrequencyCfg = JSON.parseObject(new String(file_content, 0, file_content.length));
            } catch (IOException e) {
                e.printStackTrace();
                return EraLinkUHFReaderApiError.ErrBadArg;
            }

            publishProgress(100);

            return EraLinkUHFReaderApiError.ErrNoError;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            if (100 == values[0]) {
                if (!mainActivity.channelNumberToFrequencyCfg.containsKey(mainActivity.transmitFrequencyStandard)) {
                    System.err.printf("no channel number and frequency for: %s.\n", mainActivity.transmitFrequencyStandard);
                    return;
                }

                JSONArray frequencies = mainActivity.channelNumberToFrequencyCfg.getJSONArray(mainActivity.transmitFrequencyStandard);
                if (frequencies == null || frequencies.size() == 0) {
                    System.err.printf("no frequency for: %s.\n", mainActivity.transmitFrequencyStandard);
                    return;
                }

                mainActivity.hoppingChannelList.clear();
                mainActivity.testFrequencyList.clear();
                for (int index = 0; index < frequencies.size(); ++index) {
                    mainActivity.hoppingChannelList.add(frequencies.get(index).toString());
                    mainActivity.testFrequencyList.add(frequencies.get(index).toString());
                }
                mainActivity.hoppingListAdapter.notifyDataSetChanged();
                mainActivity.testFrequencyListAdapter.notifyDataSetChanged();
            }
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);

            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity == null) {
                return;
            }

            mainActivity.hideUserDialog();
            if (result != null) {
                if (EraLinkUHFReaderApiError.ErrNoError == result) {
                    mainActivity.showUserMessageShort("加载测试配置完成。");
                } else {
                    mainActivity.showUserMessageShort(mainActivity.getResources().getString(ErrString.getErrId(result)));
                }
            }
        }
    }
}